Ver en Español
Loading Dynamic Components in Next.js
Apr 11, 2023
Updated: Jun 25, 2026

Loading Dynamic Components in Next.js

While building your application, you may run into errors like "document is not defined", "localStorage is not defined", or "window is not defined". These problems happen because there's a component that should not run on the server, but only on the client side. This usually happens for the following reasons:

  • It depends on the DOM API.
  • It depends on client side data.
  • Any other constraint specific to the component.

First things first in Next.js 16: the "use client" directive

Before jumping into Dynamic Imports, it's worth understanding the current model. In the App Router of Next.js 16 (with React 19), every component is a Server Component by default: it renders on the server and has no access to window, document, or hooks like useState or useEffect.

When a component needs the browser, you mark it as a Client Component by adding the "use client" directive at the top of the file. That's the primary mechanism today for moving code to the client:

"use client";

import { useEffect, useState } from "react";

export default function MiComponenteDificil() {
  const [width, setWidth] = useState(0);

  useEffect(() => {
    // Here we can safely use the DOM API.
    setWidth(window.innerWidth);
  }, []);

  return <p>The window width is {width}px</p>;
}

With "use client", this component now runs in the browser. But heads up: by default Next.js still pre renders it once on the server (SSR) to generate the initial HTML. If your component touches window or document directly during render, that is, outside a useEffect, you'll still see the dreaded "document is not defined". That's where Dynamic Imports come in.

How to use Dynamic Imports in Next.js?

Next.js provides a function called next/dynamic (Dynamic Imports) that lets you lazily load a component and, optionally, fully disable its server rendering with { ssr: false }.

Important in the App Router (Next.js 16): dynamic(..., { ssr: false }) is only allowed inside a Client Component. If you use it in a Server Component you'll get a build error. That's why the file where you import it must start with the "use client" directive.

First, import the next/dynamic module:

import dynamic from "next/dynamic";

Then, wrap your component with the dynamic function and use it as usual:

"use client";

// Import the 'dynamic' function from 'next/dynamic' to lazily load components.
import dynamic from "next/dynamic";

// Load 'MiComponenteDificil' dynamically and disable server rendering (SSR).
const MiComponenteDificil = dynamic(
  // Function that returns a promise with the imported component.
  () => import("../components/MiComponenteDificil"),
  // 'ssr: false' disables server rendering: the component lives only on the client.
  { ssr: false },
);

// Client component that combines normal content with the dynamic component.
export default function Page() {
  return (
    <div>
      <h1>This renders normally</h1>
      {/* This renders only on the client */}
      <MiComponenteDificil />
    </div>
  );
}

This way you can have, on the same page, components that do render on the server and others that load only on the client.

If your top level page is a Server Component, which is the norm in the App Router, don't put dynamic({ ssr: false }) there directly. Instead, extract the client part into its own file with "use client", like the example above, and import it from the Server Component as a regular component.

Conclusions

  • In Next.js 16, the first tool for browser code is the "use client" directive, which turns a component into a Client Component.
  • Dynamic Imports with { ssr: false } are the complement for cases where a component must never render on the server, because it touches window, document, or localStorage during render.
  • Remember that dynamic(..., { ssr: false }) only works inside a Client Component, so that file must include "use client".
  • You can combine server components and client only components on the same page without any issues.

Exercises to practice

  1. Create a component that uses the DOM API, for example reading window.innerWidth, mark it with "use client" and render it with Dynamic Imports and { ssr: false }.
  2. Implement a component that depends on client side data, for example localStorage, and load it with Dynamic Imports.
  3. Design a page that is a Server Component and combines server rendered content with a client only component imported dynamically.

Summary

  1. Errors like "document is not defined" are fixed by making sure the problematic component runs only on the client: start with "use client" and, if you need to avoid SSR entirely, use Dynamic Imports with { ssr: false }.
  2. The dynamic function lets you lazily load components that should not render on the server, but in the App Router it must be used inside a Client Component.
  3. You can combine server rendered components and client only components on the same page.

That's all, I hope this post is useful to you and that you can apply it to a project you have in mind. If it helped, if you want to add an opinion, or if you have any questions, don't hesitate to leave me a comment below. And remember, if you liked it, you can also share this article using the social links at the bottom. Good luck!

Sebastian Gomez

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias