12/25/2025
Link Preview and Video Embedding
Can automatically recognize and process your links
The functionality is primarily implemented using Astro Embed.
High performance, rich media embed components. For your site, from the Astro community. 
How to use?
#
You only need to insert links like in Markdown, and it will automatically handle all links. If a link matches, it will process the embedding.
[](url link)
How does it work?
#
Add Nodes Processor Link.astro
//markdoc.config.mjs
nodes: {
link: {
...nodes.link,
render: component("./src/components/markdoc/Link.astro"),
},
Add Astro components src\components\markdoc\Link.astro
//src\components\markdoc\Link.astro
---
import { LinkPreview } from "@astro-community/astro-embed-link-preview";
import { YouTube } from "@astro-community/astro-embed-youtube";
import { BlueskyPost } from "@astro-community/astro-embed-bluesky";
import { Vimeo } from "@astro-community/astro-embed-vimeo";
import X from "./X.astro";
import Bilibili from "./Bilibili.astro";
import Tiktok from "./Tiktok.astro";
interface Props {
href: string;
title: string;
}
const { href, title } = Astro.props as Props;
type EmbedProps = Readonly<{
id: string;
}>;
type EmbedComponent = (props: EmbedProps) => any;
interface EmbedRule {
match: RegExp;
Comp: EmbedComponent;
}
const rules = [
{ match: /twitter\.com|x\.com/, Comp: X },
{ match: /youtube\.com|youtu\.be/, Comp: YouTube },
{ match: /vimeo\.com/, Comp: Vimeo },
{ match: /bsky\.app/, Comp: BlueskyPost },
{ match: /bilibili\.com/, Comp: Bilibili },
{ match: /tiktok\.com/, Comp: Tiktok },
] satisfies readonly EmbedRule[];
function resolveEmbed(href: string): EmbedComponent | null {
for (const { match, Comp } of rules) {
if (match.test(href)) return Comp;
}
return null;
}
const Comp = resolveEmbed(href);
---
{Comp ? <Comp id={href} /> : <LinkPreview id={href} />}
Twitter and X
#
Twitter and X require the insertion of a JavaScript script.
//src\components\markdoc\X.astro
---
import { Tweet } from "@astro-community/astro-embed-twitter";
interface Props {
id: string;
}
const { id } = Astro.props;
---
<script async src="https://platform.twitter.com/widgets.js"></script>
<Tweet id={id} />
I also added embedded processing for Bilibili and TikTok.
Bilibili
#
//src\components\markdoc\Bilibili.astro
---
interface Props {
id: string;
}
const { id } = Astro.props;
const url = new URL(id);
const bvidMatch = id.match(/BV[a-zA-Z0-9]+/);
const bvid = bvidMatch ? bvidMatch[0] : null;
const aidMatch = id.match(/av(\d+)/i);
const aid = aidMatch ? aidMatch[1] : null;
const page = url.searchParams.get("p") || 1;
---
<iframe
style="width:100%;height:50vh;"
src={`//player.bilibili.com/player.html?${bvid ? `bvid=${bvid}` : `aid=${aid}`}&page=${page}&as_wide=1&high_quality=1&autoplay=false`}
scrolling="no"
frameborder="no"
allowfullscreen="true"
>
</iframe>
Tiktok
#
//src\components\markdoc\Tiktok.astro
---
interface Props {
id: string;
}
const { id } = Astro.props;
const match = id.match(/\/video\/(\d+)/);
const videoId = match?.[1];
const src = `https://www.tiktok.com/player/v1/${videoId}?autoplay=0&description=1&music_info=1`;
---
<iframe src={src} allowfullscreen loading="lazy" style="width:100%;height:50vh;"
></iframe>
Example
#
Play
v1.112 is live!
We’re launching Find Friends, a contact import feature that helps you find people you know on Bluesky.
Try it! In the mobile app, go to Settings → Find friends from contacts.
Read how we took a more secure approach to this than other platforms: bsky.social/about/blog/1...
December 17, 2025 at 12:49 AM UTC