Next.js Post Tag - TechOpt.io https://www.techopt.io/tag/next-js Programming, servers, Linux, Windows, macOS & more Sat, 12 Jul 2025 21:40:27 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.1 https://www.techopt.io/wp-content/uploads/2024/07/cropped-logo-1-32x32.png Next.js Post Tag - TechOpt.io https://www.techopt.io/tag/next-js 32 32 Solving Next.js dynamic() Flicker with React.lazy https://www.techopt.io/programming/solving-next-js-dynamic-flicker-with-react-lazy https://www.techopt.io/programming/solving-next-js-dynamic-flicker-with-react-lazy#respond Sat, 12 Jul 2025 21:40:24 +0000 https://www.techopt.io/?p=1039 If you’re working with the Next.js App Router and using the dynamic() function for component-level code splitting, you may have encountered an annoying issue: flickering during rendering of a conditionally-rendered dynamic component. Unfortunately, this is a known issue with the App Router and the dynamic function in Next.js. This behavior can degrade user experience, so […]

The post Solving Next.js dynamic() Flicker with React.lazy appeared first on TechOpt.

]]>
If you’re working with the Next.js App Router and using the dynamic() function for component-level code splitting, you may have encountered an annoying issue: flickering during rendering of a conditionally-rendered dynamic component. Unfortunately, this is a known issue with the App Router and the dynamic function in Next.js. This behavior can degrade user experience, so solving the Next.js dynamic flicker on your website is crucial.

In this post, I’ll break down:

  • Why next/dynamic causes flickering
  • Why it’s worse with nested dynamic components
  • When dynamic() is still safe to use
  • A practical alternative using React.lazy() and Suspense
  • What trade-offs to expect when switching

The Flickering Problem in Next.js

Using dynamic() from next/dynamic is a great way to lazy-load components and reduce your JavaScript bundle size. It also supports options like { ssr: false } to only load components on the client side.

However, when you use these components with the App Router, they often cause a flash of missing or unstyled content, especially during fast navigation or when conditionally rendering dynamic components.

Nested dynamic() calls tend to amplify this issue. For example, a parent component conditionally loading a child via dynamic(), which in turn loads another sub-component dynamically, can make the flickering more severe.

This issue has been reported in GitHub issues and community threads, but a rock-solid fix hasn’t yet made it into the framework.

Interestingly, this flicker seems to affect nested dynamic components more than top-level ones. In my testing, first-level dynamically rendered components used directly in the page file rarely exhibit the issue, which means it’s generally safe to use next/dynamic there to avoid flash of unstyled content (FOUC) during initial mount.

The Better Alternative: React.lazy() + Suspense

One workaround that has proven effective is switching from next/dynamic to native React.lazy() with Suspense. This approach introduces fewer hydration inconsistencies and minimizes flickering, even with nested lazy-loaded components.

Use next/dynamic for components initially rendered on the page, and use React.lazy() for nested components that are rendered conditionally inside those components.

Example 1: Top-level safe usage with next/dynamic

import dynamic from 'next/dynamic';
import { isSignedInAsync } from '../auth';

const PageShell = dynamic(() => import('../components/PageShell'));

export default async function Home() {
  const isSignedIn = await isSignedInAsync();

  if (isSignedIn) return null;

  return <PageShell />;
}

In this example, PageShell is conditionally rendered on the server using dynamic components. This is safe since the dynamic component is rendered with the initial HTML from the server.

Example 2: Nesting with React.lazy() and Suspense

"use client";
import dynamic from 'next/dynamic';

const NestedComponent = dynamic(() => import('./NestedComponent'));

export default function PageShell() {
  const [showNested, setShowNested] = useState(false);

  return (
    <div>
      <h1>Welcome</h1>
      <button onClick={() => setShowNested(true)}>Load Nested Component</button>
      {showNested && (
        <Suspense fallback={<div>Loading nested...</div>}>
          <NestedComponent />
        </Suspense>
      )}
    </div>
  );
}

We can safely use React.lazy() and Suspense inside our dynamically-rendered PageShell component to conditionally render our NestedComponent, and still benefit from lazy-loading and code-splitting.

If we try using the dynamic function instead of React.lazy here, we may get the Next.js dynamic flicker.

Trade-offs of Using React.lazy() Instead of dynamic

While React.lazy() and Suspense often result in smoother rendering, there are two notable downsides:

1. No Server-Side Rendering

Unlike next/dynamic, which lets you disable or enable SSR, React.lazy() only supports client-side rendering. This might hurt SEO if your component needs to be visible to crawlers.

2. Flash of Unstyled Content (FOUC) on Mount

If you do try to use React.lazy() for SSR and use it in the server-rendered HTML, React.lazy() may cause a brief flash of unstyled content because the Next.js bundler doesn’t automatically include the styles for components loaded through React.lazy() in the server-rendered HTML. This limitation can lead to inconsistent rendering.

This is why it’s best to use next/dynamic for components that are visible in the server-rendered HTML, ensuring that styles and structure are present at first paint, while reserving React.lazy() for non-critical or nested components. Using next/dynamic in the initial server-rendered HTML does not seem to cause flickering.

Final Thoughts on Preventing the Next.js Dynamic Flicker

If you’re seeing flickering with next/dynamic and conditional rendering, especially in complex nested layouts, you’re not alone. While the Next.js team continues to evolve App Router, switching to React.lazy() and Suspense where you can may provide a smoother user experience at this time.

To summarize:

  • Use next/dynamic safely for top-level page components
  • Use React.lazy() for nested dynamic imports to reduce flicker

The post Solving Next.js dynamic() Flicker with React.lazy appeared first on TechOpt.

]]>
https://www.techopt.io/programming/solving-next-js-dynamic-flicker-with-react-lazy/feed 0
Add Tailwind CSS to an Existing Next.js Project https://www.techopt.io/programming/add-tailwind-css-to-an-existing-next-js-project https://www.techopt.io/programming/add-tailwind-css-to-an-existing-next-js-project#respond Sat, 03 Aug 2024 20:00:26 +0000 http://localhost:8080/?p=88 If you have an existing Next.js project using plain CSS or CSS modules, you may decide to add Tailwind CSS at some point. During recent years, Tailwind CSS has become increasingly popular for its simplicity and ease-of-use. The good news is Tailwind can inter-op with plain CSS and CSS modules quite easily without affecting your […]

The post Add Tailwind CSS to an Existing Next.js Project appeared first on TechOpt.

]]>
If you have an existing Next.js project using plain CSS or CSS modules, you may decide to add Tailwind CSS at some point. During recent years, Tailwind CSS has become increasingly popular for its simplicity and ease-of-use.

The good news is Tailwind can inter-op with plain CSS and CSS modules quite easily without affecting your existing work. At the same time, this makes it easy to adopt gradually, even in big projects with lots of custom styling already.

Adding Tailwind CSS to the Project

To add Tailwind to an existing Next.js project, you can follow these steps.

1. Install Tailwind Package

Install the tailwindcss package with yarn or npm by running

yarn add tailwindcss

2. Add (or Update) postcss.config.js

If you don’t already have a postcss.config.js file in your project’s root, create one with the following contents:

// postcss.config.js

module.exports = {
  "plugins": [
    ["tailwindcss", {}]
  ]
}

If you do already have a postcss.config.js file, just add the tailwindcss plugin to the list of plugins like above.

3. Create Tailwind Config File

Create a tailwind.config.js or tailwind.config.ts file in your project’s root with the following contents:

// tailwind.config.ts
// This example is using TypeScript instead of plain JavaScript

import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
 
    // Or if using `src` directory:
    "./src/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

export default config;

Or if you prefer plain JavaScript:

// tailwind.config.js
// This is for JavaScript

module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
 
    // Or if using `src` directory:
    "./src/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

4. Add Tailwind Directives to globals.css File

Your Next.js project should already have a globals.css file or equivalent. Add the Tailwind directives at the top of it:

/* globals.css */

@tailwind base;
@tailwind components;
@tailwind utilities;

/* rest of css code */

5. Test it Out!

Start your dev server with yarn run dev and test out some Tailwind in your React code. You can do something like the following:

// index.tsx

export default function Home() {
  return (
    <div className="text-green-400 font-bold underline">
      Hello world!
    </div>
  )
}

You should see some green text, underlined in bold!

Inter-oping Tailwind CSS with Existing CSS & CSS Modules

If you’re already using regular CSS or CSS modules in your project, thankfully you can use Tailwind CSS class names with existing class names.

CSS modules just output a class name, so we can use our CSS module classes in addition to the Tailwind classes.

Using Tailwind Classes with CSS Modules

CSS modules just outputs unique class names. Because of this, we can use existing CSS module classes with Tailwind CSS by using template literals:

// index.tsx
import styles from '@/styles/pages/Home.module.css'

export default function Home() {
  return (
    <div className={`${styles.styled_text} text-green-400 font-bold underline`}>
      Hello world!
    </div>
  )
}

This will combine our rules from our existing CSS modules with the Tailwind classes we’ve added.

Optional: Disabling Opinionated Default Tailwind Styling Decisions

When you install Tailwind, it makes some opinionated default styling decisions on some elements to normalize the look across browsers. This is fine for new projects, but if you’ve been using CSS modules up until this point, it may mess up some of your existing styling.

To disable this behaviour, you can add the option preflight: false under the corePlugins option in your Tailwind config file, like so:

// tailwind.config.ts
// This example is using TypeScript instead of plain JavaScript

import type { Config } from "tailwindcss";

const config: Config = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
  corePlugins: {
    preflight: false
  }
}

export default config;

This will disable the default normalization styling decisions Tailwind makes, such as making all headers normal-sized text and not bold.

Remarks

  • This guide was adapted from the official Tailwind guide to go more in depth, and assumes the use of newer standards such as TypeScript.
  • Some guides mention installing postcss and autoprefixer packages in addition to Tailwind, but this isn’t necessary since Next.js comes with PostCSS support built-in.

The post Add Tailwind CSS to an Existing Next.js Project appeared first on TechOpt.

]]>
https://www.techopt.io/programming/add-tailwind-css-to-an-existing-next-js-project/feed 0