erojas.devertek.io logo
Integrating Excalidraw into a Next.js Dashboard

Why Excalidraw in Next.js?

Excalidraw is a polished whiteboard/diagramming component. In Next.js 15 apps, it’s best loaded client-side to avoid SSR pitfalls and to keep initial bundles small.

Minimal Integration Use a small wrapper component as a client component, then lazy-load it in your page.

1"use client";
2import { Excalidraw } from "@excalidraw/excalidraw";
3import "@excalidraw/excalidraw/index.css";
4import { useState } from "react";
5export default function ExcalidrawWrapper() {
6  const [initialData] = useState();
7  return (
8    <div style={{ height: "100vh", width: "100%" }}>
9      <Excalidraw
10        onChange={(elements) => {
11          console.log("Elements changed", elements);
12        }}
13        initialData={initialData}
14      />
15    </div>
16  );
17}

Performance Notes

  • Use dynamic(..., { ssr: false }) to avoid SSR crashes and reduce initial JS.

  • Keep Excalidraw CSS imported in a single place (either the wrapper or a global stylesheet) to prevent duplicates.

  • Add a lightweight loading state while the editor JS loads.

1"use client";
2import dynamic from "next/dynamic";
3const ExcalidrawWrapper = dynamic(() => import("./exalidrawWrapper"), {
4  ssr: false,
5  loading: () => (
6    <div className="flex items-center justify-center min-h-screen">
7      <p className="text-gray-600">Loading diagram editor...</p>
8    </div>
9  ),
10});
11export default function Page() {
12  return (
13    <div className="w-full h-screen">
14      <ExcalidrawWrapper />
15    </div>
16  );
17}
18

Customizing the UI (CSS-only) You can hide non-essential UI elements while preserving core editor features.

1/* Hide branding/social menu items */
2.excalidraw .dropdown-menu-group-title,
3.excalidraw .dropdown-menu [title="GitHub"],
4.excalidraw .dropdown-menu [title="X"],
5.excalidraw .dropdown-menu [title="Discord"],
6.excalidraw .dropdown-menu a[aria-label="GitHub"],
7.excalidraw .dropdown-menu a[aria-label="X"],
8.excalidraw .dropdown-menu a[aria-label="Discord"] {
9  display: none !important;
10}
11/* Hide “Find on canvas” */
12.excalidraw .dropdown-menu [title="Find on canvas"],
13.excalidraw .dropdown-menu [aria-label="Find on canvas"] {
14  display: none !important;
15}
16/* Hide Help dialog header */
17.excalidraw .HelpDialog__header {
18  display: none !important;
19}
20/* Hide Library toggle */
21.excalidraw .default-sidebar-trigger,
22.excalidraw input[aria-label="Library"] {
23  display: none !important;
24}

Data Persistence (Roadmap)

  • Start with localStorage to autosave drawings.

  • Add API routes to persist JSON in your database (e.g., Prisma/PostgreSQL).

  • Optional: enable collaboration by syncing element updates via WebSocket/Ably.

Example local persistence sketch:

1// inside ExcalidrawWrapper onChange
2onChange={(elements) => {
3  localStorage.setItem("diagram-latest", JSON.stringify(elements));
4}}

Growth Considerations

Bundle splitting is already optimized through dynamic imports, so keep the integration wrapper lightweight to ensure fast initial loads. Any additional features such as plugins or real-time collaboration should also be lazy-loaded to avoid bloating the main bundle. Secure access using your existing NextAuth session to isolate user workspaces and maintain data privacy. For a complete user workflow, include export options (PNG or SVG) along with import capabilities, enabling full portability of diagrams across environments.

Troubleshooting

A common TypeScript error occurs when using dynamic(() => <Component />) because the dynamic loader must return a promise, not JSX—so it should be written as dynamic(() => import("./Component")) instead. If UI overrides are not taking effect, it's typically due to load order or selector scope; ensure your custom CSS is loaded after Excalidraw’s default styles and that all overrides are properly scoped using the .excalidraw class to reliably target the correct elements.

SEO Metadata (optional)

interactive diagram editor to your application, and this guide walks you through how to do it using dynamic imports for optimal bundle performance, client-only rendering to avoid SSR issues, and customized CSS to align the editor with your dashboard’s visual style. By leveraging Next.js 15’s client component model and lazy-loading capabilities, you can deliver a seamless Excalidraw experience while keeping your core dashboard lightweight and responsive. (Keywords: Next.js 15, Excalidraw, dynamic import, client components, diagram editor).

Until next time keep coding!.