customization-guide > Custom Component Usage

Custom Component Usage

Notionpresso provides default components for various Notion block types. This section explains how to use and customize these default components, as well as how to create new custom components.

Examples of Using Basic Components

Heading Component

The Heading component renders Notion's title block. Here's an example of customizing the Heading component:

import React from "react";
import { Heading, type HeadingArgs } from "@notionpresso/react";


const CustomHeading: React.FC<HeadingArgs> = (props) => {
// Add underline to level 3 headings
if (props.type === "heading_3") {
return <Heading {...props} style={{ textDecoration: "underline" }} />;
}
return <Heading {...props} />;
};




export default CustomHeading;

export default CustomHeading;

In this example, we check the level property to change the style for a specific heading level.

Paragraph Component

The Paragraph component renders regular text blocks. Here's an example of customizing the Paragraph component:

import React from "react";
import { Paragraph, type ParagraphArgs } from "@notionpresso/react";


const CustomParagraph: React.FC<ParagraphArgs> = (props) => {
return (
<Paragraph
{...props}
className="custom-paragraph"
style={{ lineHeight: "1.8", marginBottom: "1em" }}
/>
);
};




export default CustomParagraph;

export default CustomParagraph;

In this example, we adjust the line spacing and bottom margin for all paragraphs and add a custom class.

Example of Customizing the Toggle Component

The Toggle component implements Notion's toggle block. Here's an advanced example of customizing the Toggle component:

import React, { useState } from "react";
import { Toggle, type ToggleArgs } from "@notionpresso/react";
import { ChevronDown, ChevronRight } from "your-icon-library";


const CustomToggle: React.FC<ToggleArgs> = (props) => {
const [isOpen, setIsOpen] = useState(false);




const handleChangeOpen = (open: boolean) => {
setIsOpen(open);
// You can add additional logic here
console.log(Toggle state changed to: ${open});
};




return (
<Toggle {...props} isOpen={isOpen} onChangeOpen={handleChangeOpen}>
<Toggle.Icon>
{isOpen ? <ChevronDown size={20} /> : <ChevronRight size={20} />}
</Toggle.Icon>
<Toggle.Content>{props.children}</Toggle.Content>
</Toggle>
);
};




export default CustomToggle;

export default CustomToggle;

In this example:

  1. We use useState to manage the open/closed state of the toggle.
  2. The handleChangeOpen function handles state changes and can implement additional logic as needed.
  3. We pass isOpen and onChangeOpen props to the Toggle component to directly control its state.
  4. We display different icons based on the isOpen state.

If no content is provided to <Toggle.Icon>, the default icon will be used.

Applying Custom Components

Here's how to use custom components in Notionpresso:

import { Notion } from "@notionpresso/react";
import CustomHeading from "./CustomHeading";
import CustomParagraph from "./CustomParagraph";
import CustomToggle from "./CustomToggle";


const MyNotionPage = ({ pageData }) => {
const customComponents = {
heading_1: CustomHeading,
heading_2: CustomHeading,
heading_3: CustomHeading,
paragraph: CustomParagraph,
toggle: CustomToggle,
};




return (
<Notion custom={customComponents}>
<Notion.Body>
<Notion.Blocks blocks={pageData.blocks} />
</Notion.Body>
</Notion>
);
};

return ( <Notion custom={customComponents}> <Notion.Body> <Notion.Blocks blocks={pageData.blocks} /> </Notion.Body> </Notion> ); };

In this example, we pass custom components to the Notion component through the custom prop. You can specify a custom component for each block type.

Creating a Completely Custom Component

You can also create entirely new components without using Notionpresso's basic components. Here's an example of creating a Todo component from scratch:

import React from "react";
import { RichText, type TodoProps } from "@notionpresso/react";


const Todo: React.FC<TodoProps> = ({ children, ...props }) => {
const {
to_do: { color, rich_text: texts, checked },
} = props;




const colorClass = color !== "default" ? notion-${color} : "";




return (
<div className={notion-block notion-to-do ${colorClass}}>
<div className="notion-to-do-item">
<div className="notion-to-do-item-content">
<input
type="checkbox"
checked={checked}
readOnly
className="notion-to-do-checkbox"
/>
<div
className={notion-to-do-text ${checked ? "notion-to-do-checked" : ""}}
>
<RichText props={texts} />
</div>
</div>
{children}
</div>
</div>
);
};




export default Todo;

export default Todo;

Points to note in this example:

  • We only import the RichText component from Notionpresso. This is to handle complex text rendering.
  • All CSS class names start with notion-. This follows Notionpresso's styling rules.
  • We add a class in the format notion-${color}. For the default color ("default"), we don't apply an additional class.
  • The component accepts a children prop to render nested content. This is to support nested structures.

Precautions When Creating Components

  1. Type Safety: Use TypeScript to clearly define prop types. Utilize the types provided by Notionpresso (e.g., HeadingArgs, ParagraphArgs).
  2. Prop Passing: Use {...props} to pass all props from custom components to basic components. This maintains type safety while preventing unnecessary prop listing.
  3. Style Overriding: Be careful not to completely replace existing styles. Instead, add className or extend the style prop.
  4. Performance Consideration: Use React.memo, useMemo, or useCallback appropriately to prevent unnecessary re-rendering.
  5. Accessibility: Consider accessibility (a11y) when creating custom components. For example, it's good to add appropriate aria- attributes to toggle buttons.
  6. Theme Support: Design custom components to be compatible with Notionpresso's theme system. Using CSS variables makes it easy to support theme switching, such as dark mode.

Applying Custom Components

Here's how to use custom components in Notionpresso:

import { Notion, type NotionPage, type CustomComponents } from 'Notionpresso';
import CustomParagraph from './CustomParagraph';
import CustomToggle from './CustomToggle';


const MyNotionPage = ({ pageData }: { pageData: NotionPage }) => {
const customComponents = useMemo<CustomComponents>(() => ({
paragraph: CustomParagraph,
toggle: CustomToggle,
}), []);




return (
<Notion custom={customComponents}>
<Notion.Cover cover={pageData.cover} />
<Notion.Body>
<Notion.Title title={pageData.title} />
<Notion.Blocks blocks={pageData.blocks} />
</Notion.Body>
</Notion>
);
};

return ( <Notion custom={customComponents}> <Notion.Cover cover={pageData.cover} /> <Notion.Body> <Notion.Title title={pageData.title} /> <Notion.Blocks blocks={pageData.blocks} /> </Notion.Body> </Notion> ); };

In this example, we pass custom components to the Notion component through the custom prop.

By following these guidelines, you can extend the functionality of Notionpresso while maintaining consistency and maintainability.