# Links and embeds

> Bookmarks, link previews, embeds, demos, child pages and links to pages.

Each example below is a real Notion block, rendered by notionic's built-in components, followed by a recipe to change it.

# Bookmark

**In Notion:** paste a link and choose **Create bookmark**, or type `/bookmark`.

[Notion API quickstart](https://developers.notion.com)

**Renders as:** a card with the page's title, description and image, which `fetchBlocks` reads from the linked page. The image moves on top when the card is narrow. See [Supported blocks](https://notionic.dev/docs/supported-blocks).

```typescript
// Customize · bookmark.tsx
import type { BlockProps } from "notionic"

export function Bookmark({ block }: BlockProps<"bookmark">) {
  const { url } = block.bookmark
  const preview = block.bookmarkPreview
  return (
    <a href={url} className="flex flex-col rounded-lg border border-border p-4 hover:bg-muted">
      <span className="font-medium">{preview?.title ?? url}</span>
      {preview?.description ? (
        <span className="line-clamp-2 text-sm text-muted-foreground">{preview.description}</span>
      ) : null}
    </a>
  )
}

// bookmarkPreviews: false skips fetching the linked pages: cards show the URL.
const blocks = await fetchBlocks(notion, pageId, { bookmarkPreviews: false })
<NotionRenderer blocks={blocks} components={{ bookmark: Bookmark }} />
```

# Link preview

**In Notion:** paste a link and choose **Paste as preview**.

> ℹ️ Link previews can't be added through Notion's API, so there's no live example here. One renders as a card like a bookmark's, from the same preview, and takes the same `components.link_preview` override.

# Embed

**In Notion:** type `/embed` and paste a link.

[An embed of a site that isn't allowed: a card.](https://www.wikipedia.org)

[An embed of a /demos/ URL: one of the site's demos. Press the button.](https://notionic.dev/demos/morph-button)

[An embed of an allowed site: a live, sandboxed iframe.](https://codepen.io/team/codepen/embed/PNaGbb?height=300)

**Renders as:** a card like a bookmark's, unless its URL ends in `/demos/<name>`, which renders one of your demos, or its site is in `embeds`, which loads it in a sandboxed iframe. `?height=` sets the height in pixels.

```typescript
// Customize · embeds.tsx
import type { DemoProps } from "notionic"

// A demo is your component; params come from the embed URL's query string.
function Counter({ params }: DemoProps) {
  return <button type="button">Start at {params.start ?? "0"}</button>
}

// An embed of …/demos/counter?start=5 renders <Counter />; codepen.io loads live.
<NotionRenderer blocks={blocks} demos={{ counter: Counter }} embeds={["codepen.io"]} />
```

# Child page

**In Notion:** type `/page` to add a page inside this one.

A child page

**Renders as:** a row with the page's icon and title. Its content isn't fetched: link the row to wherever you publish it with `pageHref`. On these docs, only other docs get a link.

```typescript
// Customize · page-links.tsx
const paths = new Map([["3fcb17268ab381d995c6ff3f97dc741b", "/blog/a-child-page"]])

// Child pages, links to pages and page mentions all go through pageHref.
<NotionRenderer blocks={blocks} pageHref={(pageId) => paths.get(pageId) ?? null} />
```

# Link to page

**In Notion:** type `/link to page` and pick a page.

> ℹ️ Link to page blocks can't be added through Notion's API, so there's no live example here. One renders like a child page: a row with the linked page's icon and title, through `pageHref`.
