This document provides guidance on how to optimize the React BlockNote package for production use.
React BlockNote has been designed with bundle size optimization in mind. The package includes most dependencies directly while keeping only a few critical ones (Radix UI components and KaTeX) as peer dependencies to maintain a balance between convenience and bundle size.
When using React BlockNote in your application, the approximate bundle sizes are:
- ES Module: ~700-900 KB (200-250 KB gzipped)
- UMD: ~700-900 KB (200-250 KB gzipped)
- CSS: ~7.3 KB (2.3 KB gzipped)
-
Use Tree Shaking
Ensure your bundler (Webpack, Rollup, Vite, etc.) has tree shaking enabled to eliminate unused code:
// vite.config.js or rollup.config.js export default { build: { rollupOptions: { treeshake: true, }, }, };
-
Lazy Load BlockNote
If you don't need the editor on initial page load, consider lazy loading it:
import React, { lazy, Suspense } from "react"; // Lazy load the editor const BlockNote = lazy(() => import("@bettaibi/react-blocknote").then((module) => ({ default: module.BlockNote, })) ); // Import CSS in your main file or in a separate CSS file import "@bettaibi/react-blocknote/styles.css"; function YourApp() { return ( <div> <Suspense fallback={<div>Loading editor...</div>}> {showEditor && <BlockNote />} </Suspense> </div> ); }
-
Use Code Splitting
If you're using a bundler with code-splitting capabilities, make sure your configuration optimally splits the editor code:
// webpack.config.js example module.exports = { optimization: { splitChunks: { chunks: "all", cacheGroups: { blocknote: { test: /[\\/]node_modules[\\/]@bettaibi[\\/]/, name: "blocknote", chunks: "all", }, }, }, }, };
-
Optimize TipTap Extensions
If you don't need all the TipTap extensions, you can create a custom build that only includes what you need:
import { BlockNote } from "@bettaibi/react-blocknote"; // Only include the extensions you need const customExtensions = { // Exclude certain extensions you don't need codeBlock: false, table: false, // other options... }; function MyEditor() { return ( <BlockNote extensions={customExtensions} // other props... /> ); }
-
Optimize Images and Media
When using the editor to embed images or other media:
- Use image CDNs or optimization services
- Consider lazy loading images within the editor content
- Implement responsive images using srcset
-
Memoize the Editor Component
Wrap your editor with React.memo to prevent unnecessary re-renders:
const MemoizedBlockNote = React.memo(BlockNote); function MyApp() { return <MemoizedBlockNote {...props} />; }
-
Debounce onChange Events
For large documents, debounce the onChange handler to prevent excessive updates:
import { useCallback } from "react"; import debounce from "lodash.debounce"; function MyEditor() { const [content, setContent] = useState(""); // Debounce the onChange handler const debouncedOnChange = useCallback( debounce((newContent) => { setContent(newContent); // Any other logic like saving to backend }, 300), [] ); return <BlockNote onChange={debouncedOnChange} value={content} />; }
-
Virtualize Large Documents
For very large documents, consider implementing virtualization to only render visible content.
-
Cache Control
Implement appropriate cache headers for the editor's static assets:
Cache-Control: max-age=31536000, immutable -
Preload Critical Resources
Add preload hints for critical editor resources:
<link rel="preload" href="/path/to/blocknote.css" as="style" />
When using SSR frameworks like Next.js, dynamically import the editor component on client-side only:
import dynamic from "next/dynamic";
const BlockNote = dynamic(
() => import("@bettaibi/react-blocknote").then((mod) => mod.BlockNote),
{ ssr: false }
);Use tools like Import Cost VSCode extension or Webpack Bundle Analyzer to monitor the impact of the editor on your bundle size.
If you need more specific optimization advice for your use case, please open an issue on our GitHub repository.