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:
- We use
useState
to manage the open/closed state of the toggle. - The
handleChangeOpen
function handles state changes and can implement additional logic as needed. - We pass
isOpen
andonChangeOpen
props to theToggle
component to directly control its state. - 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
- Type Safety: Use TypeScript to clearly define prop types. Utilize the types provided by Notionpresso (e.g.,
HeadingArgs
,ParagraphArgs
). - Prop Passing: Use
{...props}
to pass all props from custom components to basic components. This maintains type safety while preventing unnecessary prop listing. - Style Overriding: Be careful not to completely replace existing styles. Instead, add
className
or extend thestyle
prop. - Performance Consideration: Use
React.memo
,useMemo
, oruseCallback
appropriately to prevent unnecessary re-rendering. - Accessibility: Consider accessibility (a11y) when creating custom components. For example, it's good to add appropriate
aria-
attributes to toggle buttons. - 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.