Next.js 13 with Ant Design 5: Components and Pages Render Before Styles Load, Causing Jump
Asked Answered
G

6

4

I followed the antd and nextjs doc to config the project.

Added this code into the ./scripts/genAntdCss.tsx file:

import { extractStyle } from '@ant-design/static-style-extract';
import fs from 'fs';
const outputPath = './public/antd.min.css';
const css = extractStyle();
fs.writeFileSync(outputPath, css);

and this is App.tsx file:

import { StyleProvider } from '@ant-design/cssinjs';
import type { AppProps } from 'next/app';
import '../public/antd.min.css';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <StyleProvider hashPriority='high'>
      <Component {...pageProps} />
    </StyleProvider>
  );
}

these commands added to package.json file:

"predev": "ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx",
"prebuild": "ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx"

Do you have any idea to fix this?

Gaylenegayler answered 28/3, 2023 at 13:57 Comment(0)
B
15

I found a way to solve it but I had to use the new "app", "layout" and "use client" features of NextJS 13.

Well, I've found the link below where chunsch added an example in the NextJS repo: https://github.com/vercel/next.js/pull/44015/files

Also, @kiner-tang helped us to fix the layout shift while loading https://github.com/ant-design/ant-design/issues/42275#issuecomment-1555805914

It's possible but we need to do some weird stuff with the new cssinjs AntD 5 feature and with the NextJS 13 app and layout features. I would like to avoid using that but it's the way I found. Sorry.

I basically had to use the new "app" directory (experimental feature yet), create a root layout (layouts are one of the new features too), and create a RootStyleRegistry component specifying that it should be a client component with the 'use client' directive. Additionally, if you use the Layout component, you should specify the hasSider={true} prop to avoid a shifting effect in the first render.

Install @ant-design/cssinjs if you don't have it installed

  1. Create the RootStyleRegistry component

    // located at src/modules/shared/components/root-style-registry/index.tsx in my case
    
    'use client'
    import { useState, type PropsWithChildren } from 'react'
    import { useServerInsertedHTML } from 'next/navigation'
    import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs'
    
    export const RootStyleRegistry = ({ children }: PropsWithChildren) => {
      const [cache] = useState(() => createCache())
    
      useServerInsertedHTML(() => {
        return (
          <script
             dangerouslySetInnerHTML={{
              __html: `</script>${extractStyle(cache)}<script>`,
            }}
          />
        )
       })
    
       return <StyleProvider cache={cache}>{children}</StyleProvider>
    }
    
  2. Create the layout for the root page

    // src/app/layout.tsx
    import type { PropsWithChildren } from 'react'
    import { RootStyleRegistry } from '../modules/shared/components/root-style-registry'
    
    export default function RootLayout({ children }: PropsWithChildren) {
      return (
        <html lang="es">
          <head />
          <body>
            <RootStyleRegistry>{children}</RootStyleRegistry>
          </body>
        </html>
      )
    };
    
  3. Create your page component using the 'use client' directive. Remember you must name it 'page' now (like an index file inside a folder)

    // src/app/page.tsx
    
    'use client'
    import React, { Button, Card, Space, Typography } from 'antd'
    
    export default function Home() {
      return  <Button type="primary">Ant Design Button</Button>
    }
    
  4. If you use the Layout component and you are experimenting a navbar shift while loading, you should explicitly specify the 'hasSider' prop

    // src/app/components/layout.tsx
    
    export function MyLayout({ children }) {
      return (
        <Layout hasSider>
          <Sider>
            {children}
          </Sider>
        </Layout>
      )
    }
    

I also answered here: https://github.com/ant-design/ant-design/issues/38555#issuecomment-1535788843

Birth answered 5/5, 2023 at 6:50 Comment(0)
P
1

It worked for me with next.js version 13.4.8 using a slightly modified version of Chemah's answer

Below is my code with the updates to Chemah's answer in comments:

  1. In the code below, use an object with children of type React.ReactNode instead of PropsWithChildren

    "use client";
    import { useState } from "react";
    import { useServerInsertedHTML } from "next/navigation";
    import { createCache, extractStyle, StyleProvider } from "@ant-design/cssinjs";
    
    export const RootStyleRegistry = ({
      children,
    }: {
      children: React.ReactNode;  
    }) => {                    //Use ReactNode instead of PropsWithChildren
      const [cache] = useState(() => createCache());
    
      useServerInsertedHTML(() => {
        return (
          <script
            dangerouslySetInnerHTML={{
              __html: `</script>${extractStyle(cache)}<script>`,
            }}
          />
        );
      });
    
      return <StyleProvider cache={cache}>{children}</StyleProvider>;
    };
    
  2. In the layout also use React.ReactNode

     export default function RootLayout({
       children,
     }: {
       children: React.ReactNode;
     }) {
       return (
         <html lang="en">
           <body>
             <RootStyleRegistry>{children}</RootStyleRegistry>
           </body>
         </html>
       );
     }
    
Preindicate answered 13/7, 2023 at 1:3 Comment(0)
A
0

It worked for me following the instructions in the AntD docs, which it looks like you're doing.

https://ant.design/docs/react/customize-theme#server-side-render-ssr

Make sure you are installing the necessary dependencies

npm install ts-node tslib --save-dev

and adding the tsconfig.node.json, you didn't specify that you did these two steps.

Ancy answered 29/3, 2023 at 16:16 Comment(0)
T
0

https://mobile.ant.design/guide/ssr

// next.config.js
const nextConfig = {
  transpilePackages: ['antd-mobile'], // or ['antd']
};

module.exports = nextConfig;
Tonyatonye answered 17/5, 2023 at 4:58 Comment(0)
D
0

It seems that for the app router the antd team finally came up with a solution for this. Basically you just have to run

npm install @ant-design/nextjs-registry --save

and use it in your layout

import React from 'react';
import { AntdRegistry } from '@ant-design/nextjs-registry';

const RootLayout = ({ children }: React.PropsWithChildren) => (
  <html lang="en">
    <body>
      <AntdRegistry>{children}</AntdRegistry>
    </body>
  </html>
);

export default RootLayout;

I've been using it with "next": "14.1.4" and "antd": "^5.16.1" and so far is working great.

Deibel answered 5/4, 2024 at 19:22 Comment(0)
C
-1
// src/app/layout.tsx
'use client'
....
export default function RootLayout({ children }: Props) {
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
}, [])
return (
<html lang='en'>
  <head {...metadata} />
  <body className={inter.className}>
    <main className={loading ? '' : 'hidden'}>{children}</main>
  </body>
</html>
)
}
Colly answered 28/7, 2023 at 2:46 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.