You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React应用出现重复Header和Footer问题排查请求

问题:Remix应用中Header/Footer重复渲染且出现嵌套/标签

开发的简单React(Remix)应用出现Header和Footer重复渲染问题,页面DOM结构中出现嵌套的<html>与<body>标签,导致Header和Footer各显示两次。

渲染后的DOM结构

<html lang="en">
    <head></head>
    <body>
    <header>/* header content*/</header>
    <html lang="en">
        <head></head>
            <body>
                <header>/* header content*/</header>
                <h1>Welcome to the main page</h1>
                <footer>/* footer content*/</footer>
            </body>
    </html>
    <footer>/* footer content*/</footer>
    </body>
</html>

相关代码文件

app/root.tsx

import {
    Links,
    Meta,
    Outlet,
    Scripts,
    ScrollRestoration,
} from "@remix-run/react";

import Header from "./routes/components/Header"; 
import Footer from "./routes/components/Footer";

import "./tailwind.css";
import "./style.css";

export function Layout({ children }: { children: React.ReactNode }) {
    return (
        <html lang="en">
        <head>
            <meta charSet="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <Meta />
            <Links />
        </head>
        <body>
            <Header />
            {children}
            <Footer />
            <ScrollRestoration />
            <Scripts />
        </body>
    </html>
    );
}

export default function App() {
    return (
        <Layout>
            <Outlet />
        </Layout>
    );
}

app/routes/_index.tsx

import React from 'react';

const Index = () => {
    return (
        <div className='p-4'>
            <h1>Welcome to the main page</h1>   
        </div>
    );
};

export default Index;

app/routes/components/Header.tsx

import React from 'react';

const Header: React.FC = () => {
    return (
        <header>
           <div className="p-4">
              <h1 className="text-3xl">My magic app</h1>
            </div>
        </header>
    );
};

export default Header;
import React from 'react';

const Footer: React.FC = () => {
    return (
        <footer>
            <p className="py-1 text-base sm:text-sm">© 2024, MadFox. All rights reserved.</p>
        </footer>
    );
};
export default Footer;

测试验证现象

  • 删除app/root.tsx中Layout函数内的<Header />,页面仅显示两个Footer,无Header内容;
  • 删除Layout函数内的<Footer />,页面仅显示两个Header,无Footer内容;
  • 同时删除<Header />和<Footer />,页面无内容,内部<body>为空。

问题原因及修复方案

核心原因

Remix框架的约定是:如果在root.tsx中导出了Layout组件,它会自动作为根布局包裹默认导出的App组件的内容。但当前代码中,默认导出的App组件手动将<Outlet />包裹在了<Layout>中,导致Layout被双重渲染:

  1. Remix自动用Layout包裹App组件的返回内容;
  2. App组件内部又再次渲染了Layout,进而嵌套出完整的<html>/<body>结构。

这就造成了外层<html>的<body>里先渲染Header,然后内层嵌套的<html>里又渲染了Header、页面内容、Footer,最后外层<body>再渲染Footer,最终出现嵌套DOM结构和重复的Header/Footer。

修复方案

修改app/root.tsx的默认导出App组件,直接返回<Outlet />即可,不需要手动包裹<Layout>:

export default function App() {
    return <Outlet />;
}

这样Remix会自动使用导出的Layout组件来包裹App的内容,避免双重渲染,最终得到正常的DOM结构:外层单一的<html>/<body>,包含一次Header、页面内容、Footer。


内容的提问来源于stack exchange,提问作者Anna Andreevna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 11:57:19