Skip to main content

Customizing Block Rendering

Noxion includes a flexible system for customizing how Notion content is rendered. You can replace default components for images and links, or completely override the rendering logic for specific Notion block types like callouts, code blocks, or images.


The components Prop

The NotionRenderer component accepts a components prop to inject custom React components into the rendering tree.

import { NotionRenderer } from '@noxion/notion-renderer';
import { MyImage } from './components/MyImage';
import { MyLink } from './components/MyLink';

export function MyPage({ recordMap }) {
return (
<NotionRenderer
recordMap={recordMap}
components={{
Image: MyImage,
Link: MyLink,
PageLink: MyLink, // Used for internal Notion page links
}}
/>
);
}

Common Component Overrides

ComponentDescriptionProps
ImageReplaces all images in the document.src, alt, width, height, className
LinkReplaces external URLs in rich text.href, className, children
PageLinkReplaces internal Notion page links.href, className, children
nextImageSpecifically for Next.js next/image integration.Record<string, unknown>

Overriding Specific Blocks

The blockOverrides property within the components prop targets specific Notion block types. This changes the UI of a specific block without affecting others.

Example: Custom Callout

This example replaces the default Notion callout with a custom styled component.

import { NotionRenderer, NotionBlockProps } from '@noxion/notion-renderer';

const CustomCallout = ({ block, children }: NotionBlockProps) => {
const icon = block.format?.page_icon;

return (
<div className="custom-callout">
<span className="callout-icon">{icon}</span>
<div className="callout-content">{children}</div>
</div>
);
};

export function MyPage({ recordMap }) {
return (
<NotionRenderer
recordMap={recordMap}
components={{
blockOverrides: {
callout: CustomCallout,
},
}}
/>
);
}

The NotionBlockProps Interface

Every custom block component receives these props:

PropTypeDescription
blockBlockThe raw Notion block data.
blockIdstringThe UUID of the block.
levelnumberThe nesting depth of the block.
childrenReactNodeThe rendered children of the block.

URL Mapping

Customize page and image URL resolution using mapPageUrl and mapImageUrl.

Mapping Page URLs

By default, internal links use the Notion page ID. You can map these to custom paths.

<NotionRenderer
recordMap={recordMap}
mapPageUrl={(pageId) => `/blog/${pageId}`}
/>

Mapping Image URLs

Useful for proxying images or adding transformation parameters (e.g., for Cloudinary or Imgix).

<NotionRenderer
recordMap={recordMap}
mapImageUrl={(url, block) => {
if (url.startsWith('https://s3.us-west-2.amazonaws.com')) {
return `https://my-proxy.com/image?url=${encodeURIComponent(url)}`;
}
return url;
}}
/>

Block Actions

The showBlockActions prop (v0.4+) enables interactive buttons on blocks, such as "Copy Code" or "Share Link".

<NotionRenderer
recordMap={recordMap}
showBlockActions={true} // Enable for all supported blocks
/>

// Or selectively enable per block type
<NotionRenderer
recordMap={recordMap}
showBlockActions={(blockType) => blockType === 'code'}
/>

Combining Overrides and Plugins

Block overrides and Renderer Plugins work together to provide a layered customization system.

  1. Plugins run first. If a plugin's blockOverride hook returns a component, it takes precedence.
  2. blockOverrides prop is checked next if no plugin intercepted the block.
  3. Default Renderers are used as a final fallback.

Use blockOverrides for simple UI replacements and Renderer Plugins when you need to transform data or apply logic across multiple block types.


Real-World Examples

Code Block with Copy Button

Build a fully custom code block experience if the default showBlockActions is insufficient:

const CustomCode = ({ block }: NotionBlockProps) => {
const content = block.properties?.title?.[0]?.[0] || '';

return (
<div className="code-container">
<pre><code>{content}</code></pre>
<button onClick={() => navigator.clipboard.writeText(content)}>
Copy
</button>
</div>
);
};

Image with Lightbox

Wrap the default image rendering with a lightbox component.

const LightboxImage = (props: any) => {
return (
<div className="lightbox-wrapper">
<img {...props} onClick={() => openLightbox(props.src)} />
</div>
);
};

// Register in components.Image

See NotionRenderer API Reference for the full list of available props and types.