NextJS中如何为404页面创建独立自定义布局?
问题原因
在Next.js App Router中,not-found.tsx会被自动嵌套在根layout.tsx内部执行,所以根布局里的Footer等组件会始终渲染,哪怕你给404页面套了自定义布局。同时你当前的JFCNotFound组件重复渲染了<html>和<body>标签,会导致DOM嵌套错误,这也是需要修正的点。
方案一:在根布局中跳过404页面的Footer渲染
这种方法通过上下文标记404页面状态,让根布局根据状态决定是否渲染Footer:
- 创建NotFound上下文文件
// app/NotFoundContext.tsx 'use client'; import { createContext, useContext, ReactNode } from 'react'; const NotFoundContext = createContext<boolean>(false); export function NotFoundProvider({ children, isNotFound }: { children: ReactNode; isNotFound: boolean }) { return ( <NotFoundContext.Provider value={isNotFound}> {children} </NotFoundContext.Provider> ); } export function useIsNotFound() { return useContext(NotFoundContext); }
- 修改
not-found.tsx,添加上下文包裹
// app/not-found.tsx import Link from 'next/link'; import JFCNotFoundLayout from '@/components/JFCLayouts/JFCNotFound'; import { NotFoundProvider } from './NotFoundContext'; export default function NotFound() { return ( <NotFoundProvider isNotFound={true}> <JFCNotFoundLayout> <div className='text-center'> <h2 className='text-3xl'>There was a problem.</h2> <p>We could not find the page you were looking for.</p> <p>Go back to the <Link href="/">dashboard</Link></p> </div> </JFCNotFoundLayout> </NotFoundProvider> ); }
- 修正根
layout.tsx,根据状态控制Footer渲染
// app/layout.tsx 'use client'; import type { Metadata } from 'next'; import Head from 'next/head'; import '../styles/globals.css'; import { Footer } from '../components/Footer'; import JFCProvider from '../redux/JFCProvider'; import { useIsNotFound } from './NotFoundContext'; export const metadata: Metadata = { title: 'My App', description: 'My App' }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { const isNotFound = useIsNotFound(); return ( <html lang="en"> <Head> <meta http-equiv="ScreenOrientation" content="autoRotate:disabled" /> </Head> <body suppressHydrationWarning={true}> <JFCProvider> {children} {!isNotFound && <Footer />} </JFCProvider> </body> </html> ); }
- 修正
JFCNotFound组件,移除重复的<html>和<body>标签
// components/JFCLayouts/JFCNotFound.tsx 'use client'; import React from 'react'; import '../../styles/globals.css'; import './style.css'; const JFCNotFound = ({ children, }: { children: React.ReactNode; }) => { return ( <div className="jfc not-found"> {children} </div> ); }; export default JFCNotFound;
方案二:使用分组布局分离页面(推荐)
这种方法符合App Router的设计逻辑,将需要Footer的页面放在分组目录中,404页面独立在根目录,避免继承多余布局:
- 创建分组目录
app/(main),将所有正常页面(如page.tsx、dashboard/page.tsx等)移入该目录 - 在
app/(main)下创建专属布局,放置Footer等组件
// app/(main)/layout.tsx import { Footer } from '../../components/Footer'; export default function MainLayout({ children, }: { children: React.ReactNode; }) { return ( <> {children} <Footer /> </> ); }
- 简化根
layout.tsx,只保留全局基础包裹
// app/layout.tsx import type { Metadata } from 'next'; import Head from 'next/head'; import '../styles/globals.css'; import JFCProvider from '../redux/JFCProvider'; export const metadata: Metadata = { title: 'My App', description: 'My App' }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <Head> <meta http-equiv="ScreenOrientation" content="autoRotate:disabled" /> </Head> <body suppressHydrationWarning={true}> <JFCProvider> {children} </JFCProvider> </body> </html> ); }
- 修正
JFCNotFound组件(同方案一的步骤4)
此时not-found.tsx位于根目录,只会继承根布局,不会加载(main)分组里的Footer组件。
内容的提问来源于stack exchange,提问作者rochellecanale
相关产品推荐
相关产品推荐

