• English
  • ProLayout - Advanced Layout

    ProLayout provides a standard, yet flexible, middle and backend layout, with one-click layout switching and automatic menu generation. It can be used with PageContainer to automatically generate breadcrumbs, page headers, and provide a low-cost solution to access the footer toolbar.

    Code Demo

    Enum property switch

    Use a segmented control to toggle layout, contentWidth, siderMenuType, and other enum props side by side.

    Enum property switch
    import {
      GithubFilled,
      InfoCircleFilled,
      QuestionCircleFilled,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Segmented, Space, theme } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      const { token } = theme.useToken();
    
      const [layout, setLayout] = useState<ProSettings['layout']>('mix');
      const [navTheme, setNavTheme] = useState<ProSettings['navTheme']>('light');
      const [contentWidth, setContentWidth] = useState<'Fluid' | 'Fixed'>('Fluid');
      const [siderMenuType, setSiderMenuType] = useState<'sub' | 'group'>('group');
    
      const [pathname, setPathname] = useState('/welcome');
    
      return (
        <div
          style={{
            height: 700,
            overflow: 'auto',
            border: `1px solid ${token.colorBorderSecondary}`,
            borderRadius: token.borderRadius,
          }}
        >
          <ProLayout
            {...defaultProps}
            layout={layout}
            navTheme={navTheme}
            contentWidth={contentWidth}
            siderMenuType={siderMenuType}
            fixSiderbar
            location={{ pathname }}
            menu={{ collapsedShowGroupTitle: true }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: '书琰',
            }}
            actionsRender={() => [
              <InfoCircleFilled key="info" />,
              <QuestionCircleFilled key="question" />,
              <GithubFilled key="github" />,
            ]}
            menuItemRender={(item, dom) => (
              <div onClick={() => setPathname(item.path || '/welcome')}>
                {dom}
              </div>
            )}
          >
            <PageContainer>
              <ProCard style={{ marginBlockEnd: 16 }} variant="outlined">
                <Space orientation="vertical" size={12} style={{ width: '100%' }}>
                  <Space>
                    <span>layout 导航模式:</span>
                    <Segmented
                      value={layout}
                      onChange={(v) => setLayout(v as any)}
                      options={[
                        { label: '侧栏 side', value: 'side' },
                        { label: '顶部 top', value: 'top' },
                        { label: '混合 mix', value: 'mix' },
                      ]}
                    />
                  </Space>
                  <Space>
                    <span>navTheme 导航主题:</span>
                    <Segmented
                      value={navTheme}
                      onChange={(v) => setNavTheme(v as any)}
                      options={[
                        { label: '亮色 light', value: 'light' },
                        { label: '暗色 realDark', value: 'realDark' },
                      ]}
                    />
                  </Space>
                  <Space>
                    <span>contentWidth 内容宽度:</span>
                    <Segmented
                      value={contentWidth}
                      onChange={(v) => setContentWidth(v as any)}
                      options={[
                        { label: '流式 Fluid', value: 'Fluid' },
                        { label: '定宽 Fixed', value: 'Fixed' },
                      ]}
                    />
                  </Space>
                  <Space>
                    <span>siderMenuType 菜单模式:</span>
                    <Segmented
                      value={siderMenuType}
                      onChange={(v) => setSiderMenuType(v as any)}
                      options={[
                        { label: '子菜单 sub', value: 'sub' },
                        { label: '分组 group', value: 'group' },
                      ]}
                    />
                  </Space>
                </Space>
              </ProCard>
              <ProCard>
                当前配置:layout=<b>{layout}</b>、navTheme=<b>{navTheme}</b>、
                contentWidth=<b>{contentWidth}</b>、siderMenuType=
                <b>{siderMenuType}</b>
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default Demo;
    

    Basic Usage

    Basic Usage
    import {
      CaretDownFilled,
      DoubleRightOutlined,
      GithubFilled,
      InfoCircleFilled,
      LogoutOutlined,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import {
      PageContainer,
      ProCard,
      ProConfigProvider,
      ProLayout,
      SettingDrawer,
    } from '@ant-design/pro-components';
    import { css } from '@emotion/css';
    import {
      Button,
      ConfigProvider,
      Divider,
      Dropdown,
      Input,
      Popover,
      theme,
    } from 'antd';
    import React, { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const SOLUTIONS = {
      金融行业: [
        '智能风控平台',
        '供应链金融',
        '数字银行核心',
        '保险理赔系统',
        '资产管理中台',
        '合规审计平台',
      ],
      通用方案: [
        '统一权限中心',
        '数据可视化引擎',
        '微服务治理框架',
        '低代码搭建平台',
        '智能客服系统',
        'DevOps 工具链',
      ],
    };
    
    const HOT_PRODUCTS = [
      { name: 'Ant Design Pro', desc: '开箱即用的中台前端解决方案' },
      { name: '数据可视化引擎', desc: '企业级数据看板与图表分析工具' },
      { name: '微服务治理框架', desc: '基于 K8s 的服务注册与发现' },
    ];
    
    const SolutionItem: React.FC<{ children: React.ReactNode }> = (props) => {
      const { token } = theme.useToken();
      return (
        <div
          className={css`
            color: ${token.colorTextSecondary};
            font-size: 14px;
            cursor: pointer;
            line-height: 22px;
            margin-bottom: 8px;
            &:hover {
              color: ${token.colorPrimary};
            }
          `}
          style={{
            width: '33.33%',
          }}
        >
          {props.children}
          <DoubleRightOutlined
            style={{
              marginInlineStart: 4,
            }}
          />
        </div>
      );
    };
    
    const SolutionCategory: React.FC<{
      title: string;
      items: string[];
      style?: React.CSSProperties;
    }> = (props) => {
      const { token } = theme.useToken();
    
      return (
        <div
          style={{
            width: '100%',
            ...props.style,
          }}
        >
          <div
            style={{
              fontSize: 16,
              color: token.colorTextHeading,
              lineHeight: '24px',
              fontWeight: 500,
              marginBlockEnd: 16,
            }}
          >
            {props.title}
          </div>
          <div
            style={{
              display: 'flex',
              flexWrap: 'wrap',
            }}
          >
            {props.items.map((item) => (
              <SolutionItem key={item}>{item}</SolutionItem>
            ))}
          </div>
        </div>
      );
    };
    
    const MenuCard = () => {
      const { token } = theme.useToken();
      return (
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
          }}
        >
          <Divider
            style={{
              height: '1.5em',
            }}
            orientation="vertical"
          />
          <Popover
            placement="bottom"
            styles={{
              root: {
                width: 'calc(100vw - 24px)',
                padding: '24px',
                paddingTop: 8,
                height: '307px',
                borderRadius: '0 0 6px 6px',
              },
            }}
            content={
              <div style={{ display: 'flex', padding: '32px 40px' }}>
                <div style={{ flex: 1 }}>
                  <SolutionCategory
                    title="金融行业"
                    items={SOLUTIONS['金融行业']}
                  />
                  <SolutionCategory
                    title="通用方案"
                    items={SOLUTIONS['通用方案']}
                    style={{ marginBlockStart: 32 }}
                  />
                </div>
    
                <div
                  style={{
                    width: '308px',
                    borderInlineStart: '1px solid ' + token.colorBorder,
                    paddingInlineStart: 16,
                  }}
                >
                  <div
                    className={css`
                      font-size: 14px;
                      color: ${token.colorText};
                      line-height: 22px;
                    `}
                  >
                    热门产品
                  </div>
                  {HOT_PRODUCTS.map((product) => (
                    <div
                      key={product.name}
                      className={css`
                        border-radius: 4px;
                        padding: 16px;
                        margin-top: 4px;
                        display: flex;
                        cursor: pointer;
                        &:hover {
                          background-color: ${token.colorBgTextHover};
                        }
                      `}
                    >
                      <img src="https://gw.alipayobjects.com/zos/antfincdn/6FTGmLLmN/bianzu%25252013.svg" alt="" />
                      <div
                        style={{
                          marginInlineStart: 14,
                        }}
                      >
                        <div
                          className={css`
                            font-size: 14px;
                            color: ${token.colorText};
                            line-height: 22px;
                          `}
                        >
                          {product.name}
                        </div>
                        <div
                          className={css`
                            font-size: 12px;
                            color: ${token.colorTextSecondary};
                            line-height: 20px;
                          `}
                        >
                          {product.desc}
                        </div>
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            }
          >
            <div
              style={{
                color: token.colorTextHeading,
                fontWeight: 500,
                cursor: 'pointer',
                display: 'flex',
                gap: 4,
                paddingInlineStart: 8,
                paddingInlineEnd: 12,
                alignItems: 'center',
              }}
              className={css`
                &:hover {
                  background-color: ${token.colorBgTextHover};
                }
              `}
            >
              <span> 企业级资产中心</span>
              <CaretDownFilled />
            </div>
          </Popover>
        </div>
      );
    };
    
    const SearchInput = () => {
      const { token } = theme.useToken();
      return (
        <div
          key="SearchOutlined"
          aria-hidden
          style={{
            display: 'flex',
            alignItems: 'center',
            marginInlineEnd: 24,
          }}
          onMouseDown={(e) => {
            e.stopPropagation();
            e.preventDefault();
          }}
        >
          <Input
            style={{
              borderRadius: 4,
              marginInlineEnd: 12,
              backgroundColor: token.colorBgTextHover,
            }}
            prefix={
              <SearchOutlined
                style={{
                  color: token.colorTextLightSolid,
                }}
              />
            }
            placeholder="搜索方案"
            variant="borderless"
          />
          <PlusCircleFilled
            style={{
              color: token.colorPrimary,
              fontSize: 24,
            }}
          />
        </div>
      );
    };
    
    const Demo = () => {
      const [settings, setSetting] = useState<Partial<ProSettings> | undefined>({
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      });
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
      const [num, setNum] = useState(40);
      if (typeof document === 'undefined') {
        return <div />;
      }
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
            overflow: 'auto',
          }}
        >
          <ProConfigProvider hashed={false}>
            <ConfigProvider
              getTargetContainer={() => {
                return document.getElementById('test-pro-layout') || document.body;
              }}
            >
              <ProLayout
                prefixCls="my-prefix"
                bgLayoutImgList={[
                  {
                    src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                    left: 85,
                    bottom: 100,
                    height: '303px',
                  },
                  {
                    src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                    bottom: -68,
                    right: -45,
                    height: '303px',
                  },
                  {
                    src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                    bottom: 0,
                    left: 0,
                    width: '331px',
                  },
                ]}
                {...defaultProps}
                location={{
                  pathname,
                }}
                token={{
                  header: {
                    colorBgMenuItemSelected: 'rgba(0,0,0,0.04)',
                  },
                }}
                siderMenuType="group"
                menu={{
                  collapsedShowGroupTitle: true,
                }}
                avatarProps={{
                  src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
                  size: 'small',
                  title: '书琰',
                  render: (props, dom) => {
                    return (
                      <Dropdown
                        menu={{
                          items: [
                            {
                              key: 'logout',
                              icon: <LogoutOutlined />,
                              label: '退出登录',
                            },
                          ],
                        }}
                      >
                        {dom}
                      </Dropdown>
                    );
                  },
                }}
                actionsRender={(props) => {
                  if (props.isMobile) return [];
                  if (typeof window === 'undefined') return [];
                  return [
                    props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                      <SearchInput key="SearchInput" />
                    ) : undefined,
                    <InfoCircleFilled key="InfoCircleFilled" />,
                    <QuestionCircleFilled key="QuestionCircleFilled" />,
                    <GithubFilled key="GithubFilled" />,
                  ];
                }}
                headerTitleRender={(logo, title, _) => {
                  const defaultDom = (
                    <button
                      type="button"
                      style={{
                        background: 'none',
                        border: 'none',
                        padding: 0,
                        font: 'inherit',
                        cursor: 'pointer',
                      }}
                    >
                      {logo}
                      {title}
                    </button>
                  );
                  if (typeof window === 'undefined') return defaultDom;
                  if (document.body.clientWidth < 1400) {
                    return defaultDom;
                  }
                  if (_.isMobile) return defaultDom;
                  return (
                    <>
                      {defaultDom}
                      <MenuCard />
                    </>
                  );
                }}
                menuFooterRender={(props) => {
                  if (props?.collapsed) return undefined;
                  return (
                    <div
                      style={{
                        textAlign: 'center',
                        paddingBlockStart: 12,
                      }}
                    >
                      <div>© 2021 Made with love</div>
                      <div>by Ant Design</div>
                    </div>
                  );
                }}
                onMenuHeaderClick={demoOnMenuHeaderClick}
                menuItemRender={(item, dom) => (
                  <div
                    role="button"
                    tabIndex={0}
                    onClick={() => {
                      setPathname(item.path || '/welcome');
                    }}
                    onKeyDown={(e) => {
                      if (e.key === 'Enter' || e.key === ' ') {
                        e.preventDefault();
                        setPathname(item.path || '/welcome');
                      }
                    }}
                  >
                    {dom}
                  </div>
                )}
                {...settings}
              >
                <PageContainer
                  token={{
                    paddingInlinePageContainerContent: num,
                  }}
                  extra={[
                    <Button key="3">操作</Button>,
                    <Button key="2">操作</Button>,
                    <Button
                      key="1"
                      type="primary"
                      onClick={() => {
                        setNum(num > 0 ? 0 : 40);
                      }}
                    >
                      主操作
                    </Button>,
                  ]}
                  subTitle="简单的描述"
                  footer={[
                    <Button key="3">重置</Button>,
                    <Button key="2" type="primary">
                      提交
                    </Button>,
                  ]}
                >
                  <ProCard
                    style={{
                      height: '200vh',
                      minHeight: 800,
                    }}
                  >
                    <div />
                  </ProCard>
                </PageContainer>
    
                <SettingDrawer
                  pathname={pathname}
                  enableDarkTheme
                  getContainer={(e: any) => {
                    if (typeof window === 'undefined') return e;
                    return document.getElementById('test-pro-layout');
                  }}
                  settings={settings}
                  onSettingChange={(changeSetting) => {
                    setSetting(changeSetting);
                  }}
                  disableUrlParams={false}
                />
              </ProLayout>
            </ConfigProvider>
          </ProConfigProvider>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Common layout toggles

    Layout prop toggles
    import { GithubOutlined } from '@ant-design/icons';
    import {
      DefaultFooter,
      PageContainer,
      ProLayout,
    } from '@ant-design/pro-components';
    import { Switch } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      const [loading, setLoading] = useState(false);
      const [collapsed, setCollapsed] = useState(true);
      const [menu, setMenu] = useState(true);
      const [header, setHeader] = useState(true);
      const [footer, setFooter] = useState(true);
      const [menuHeader, setMenuHeader] = useState(true);
      const [right, setRight] = useState(true);
      const [pure, setPure] = useState(false);
      const [collapsedButtonRender, setCollapsedButtonRender] = useState(true);
      return (
        <>
          <Switch
            checked={loading}
            onChange={(e) => setLoading(e)}
            style={{
              margin: 8,
            }}
          />
          loading 状态
          <Switch
            checked={collapsed}
            onChange={(e) => setCollapsed(e)}
            style={{
              margin: 8,
            }}
          />
          折叠layout
          <Switch
            checked={menu}
            onChange={(e) => setMenu(e)}
            style={{
              margin: 8,
            }}
          />
          显示菜单
          <Switch
            checked={collapsedButtonRender}
            onChange={(e) => setCollapsedButtonRender(e)}
            style={{
              margin: 8,
            }}
          />
          显示折叠按钮
          <Switch
            checked={header}
            onChange={(e) => setHeader(e)}
            style={{
              margin: 8,
            }}
          />
          显示顶栏
          <Switch
            checked={menuHeader}
            onChange={(e) => setMenuHeader(e)}
            style={{
              margin: 8,
            }}
          />
          显示菜单头
          <Switch
            checked={footer}
            onChange={(e) => setFooter(e)}
            style={{
              margin: 8,
            }}
          />
          显示页脚
          <Switch
            checked={right}
            onChange={(e) => setRight(e)}
            style={{
              margin: 8,
            }}
          />
          显示顶栏右侧
          <Switch
            checked={pure}
            onChange={(e) => setPure(e)}
            style={{
              margin: 8,
            }}
          />
          清爽模式
          <br />
          <br />
          <ProLayout
            {...defaultProps}
            style={{
              height: '100vh',
            }}
            menuHeaderRender={menuHeader ? undefined : false}
            headerRender={header ? undefined : false}
            collapsedButtonRender={collapsedButtonRender ? undefined : false}
            menuRender={(_, dom) => (menu ? dom : null)}
            breakpoint={false}
            collapsed={collapsed}
            loading={loading}
            onCollapse={setCollapsed}
            avatarProps={{
              src: 'https://avatars1.githubusercontent.com/u/8186664?s=460&v=4',
            }}
            location={{
              pathname: '/welcome',
            }}
            pure={pure}
            footerRender={() =>
              footer ? (
                <DefaultFooter
                  links={[
                    {
                      key: 'Ant Design Pro',
                      title: 'Ant Design Pro',
                      href: 'https://pro.ant.design',
                      blankTarget: true,
                    },
                    {
                      key: 'github',
                      title: <GithubOutlined />,
                      href: 'https://github.com/ant-design/ant-design-pro',
                      blankTarget: true,
                    },
                    {
                      key: 'Ant Design',
                      title: 'Ant Design',
                      href: 'https://ant.design',
                      blankTarget: true,
                    },
                  ]}
                  copyright="2024 星辰科技体验技术部出品"
                />
              ) : null
            }
          >
            <PageContainer content="欢迎使用">Hello World</PageContainer>
          </ProLayout>
        </>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Sidebar Navigation (Default)
    import {
      GithubFilled,
      InfoCircleFilled,
      QuestionCircleFilled,
    } from '@ant-design/icons';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            siderWidth={216}
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              title: '书琰',
              size: 'small',
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuItemRender={(item, dom) => (
              <div
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </div>
            )}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '100vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Top Navigation
    import {
      CaretDownFilled,
      DoubleRightOutlined,
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { css } from '@emotion/css';
    import { Divider, Input, Popover, theme } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const SOLUTIONS = {
      金融行业: [
        '智能风控平台',
        '供应链金融',
        '数字银行核心',
        '保险理赔系统',
        '资产管理中台',
        '合规审计平台',
      ],
      通用方案: [
        '统一权限中心',
        '数据可视化引擎',
        '微服务治理框架',
        '低代码搭建平台',
        '智能客服系统',
        'DevOps 工具链',
      ],
    };
    
    const HOT_PRODUCTS = [
      { name: 'Ant Design Pro', desc: '开箱即用的中台前端解决方案' },
      { name: '数据可视化引擎', desc: '企业级数据看板与图表分析工具' },
      { name: '微服务治理框架', desc: '基于 K8s 的服务注册与发现' },
    ];
    
    const SolutionItem: React.FC<{ children: React.ReactNode }> = (props) => {
      const { token } = theme.useToken();
      return (
        <div
          className={css`
            color: ${token.colorTextSecondary};
            font-size: 14px;
            cursor: pointer;
            line-height: 22px;
            margin-bottom: 8px;
            &:hover {
              color: ${token.colorPrimary};
            }
          `}
          style={{
            width: '33.33%',
          }}
        >
          {props.children}
          <DoubleRightOutlined
            style={{
              marginInlineStart: 4,
            }}
          />
        </div>
      );
    };
    
    const SolutionCategory: React.FC<{
      title: string;
      items: string[];
      style?: React.CSSProperties;
    }> = (props) => {
      const { token } = theme.useToken();
    
      return (
        <div
          style={{
            width: '100%',
            ...props.style,
          }}
        >
          <div
            style={{
              fontSize: 16,
              color: token.colorTextHeading,
              lineHeight: '24px',
              fontWeight: 500,
              marginBlockEnd: 16,
            }}
          >
            {props.title}
          </div>
          <div
            style={{
              display: 'flex',
              flexWrap: 'wrap',
            }}
          >
            {props.items.map((item) => (
              <SolutionItem key={item}>{item}</SolutionItem>
            ))}
          </div>
        </div>
      );
    };
    
    const MenuCard = () => {
      const { token } = theme.useToken();
      return (
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
          }}
        >
          <Divider
            style={{
              height: '1.5em',
            }}
            orientation="vertical"
          />
          <Popover
            placement="bottom"
            styles={{
              root: {
                width: 'calc(100vw - 24px)',
                padding: '24px',
                paddingTop: 8,
                height: '307px',
                borderRadius: '0 0 6px 6px',
              },
            }}
            content={
              <div style={{ display: 'flex', padding: '32px 40px' }}>
                <div style={{ flex: 1 }}>
                  <SolutionCategory
                    title="金融行业"
                    items={SOLUTIONS['金融行业']}
                  />
                  <SolutionCategory
                    title="通用方案"
                    items={SOLUTIONS['通用方案']}
                    style={{ marginBlockStart: 32 }}
                  />
                </div>
    
                <div
                  style={{
                    width: '308px',
                    borderInlineStart: '1px solid ' + token.colorBorder,
                    paddingInlineStart: 16,
                  }}
                >
                  <div
                    className={css`
                      font-size: 14px;
                      color: ${token.colorText};
                      line-height: 22px;
                    `}
                  >
                    热门产品
                  </div>
                  {HOT_PRODUCTS.map((product) => (
                    <div
                      key={product.name}
                      className={css`
                        border-radius: 4px;
                        padding: 16px;
                        margin-top: 4px;
                        display: flex;
                        cursor: pointer;
                        &:hover {
                          background-color: ${token.colorBgTextHover};
                        }
                      `}
                    >
                      <img src="https://gw.alipayobjects.com/zos/antfincdn/6FTGmLLmN/bianzu%25252013.svg" alt="" />
                      <div
                        style={{
                          marginInlineStart: 14,
                        }}
                      >
                        <div
                          className={css`
                            font-size: 14px;
                            color: ${token.colorText};
                            line-height: 22px;
                          `}
                        >
                          {product.name}
                        </div>
                        <div
                          className={css`
                            font-size: 12px;
                            color: ${token.colorTextSecondary};
                            line-height: 20px;
                          `}
                        >
                          {product.desc}
                        </div>
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            }
          >
            <div
              style={{
                color: token.colorTextHeading,
                fontWeight: 500,
                cursor: 'pointer',
                display: 'flex',
                gap: 4,
                paddingInlineStart: 8,
                paddingInlineEnd: 12,
                alignItems: 'center',
              }}
              className={css`
                &:hover {
                  background-color: ${token.colorBgTextHover};
                }
              `}
            >
              <span> 企业级资产中心</span>
              <CaretDownFilled />
            </div>
          </Popover>
        </div>
      );
    };
    
    const Demo = () => {
      const settings: ProSettings | undefined = {
        fixSiderbar: true,
        layout: 'top',
        splitMenus: true,
      };
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: '书琰',
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                        backgroundColor: 'rgba(0,0,0,0.03)',
                      }}
                      prefix={
                        <SearchOutlined
                          style={{
                            color: 'rgba(0, 0, 0, 0.15)',
                          }}
                        />
                      }
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuItemRender={(item, dom) => (
              <div
                role="button"
                tabIndex={0}
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
                onKeyDown={(e) => {
                  if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    setPathname(item.path || '/welcome');
                  }
                }}
              >
                {dom}
              </div>
            )}
            headerTitleRender={(logo, title, _) => {
              const defaultDom = (
                <button
                  type="button"
                  style={{
                    background: 'none',
                    border: 'none',
                    padding: 0,
                    font: 'inherit',
                    cursor: 'pointer',
                  }}
                  onClick={() => console.log('headerTitle clicked')}
                >
                  {logo}
                  {title}
                </button>
              );
              if (
                typeof document === 'undefined' ||
                document.body.clientWidth < 1400
              ) {
                return defaultDom;
              }
              if (_.isMobile) return defaultDom;
              return (
                <>
                  {defaultDom}
                  <MenuCard />
                </>
              );
            }}
            {...settings}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '100vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Mixed Navigation
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const settings: ProSettings | undefined = {
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      };
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: '书琰',
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                        backgroundColor: 'rgba(0,0,0,0.03)',
                      }}
                      prefix={
                        <SearchOutlined
                          style={{
                            color: 'rgba(0, 0, 0, 0.15)',
                          }}
                        />
                      }
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <div
                  style={{
                    textAlign: 'center',
                    paddingBlockStart: 12,
                  }}
                >
                  <div>© 2021 Made with love</div>
                  <div>by Ant Design</div>
                </div>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <div
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </div>
            )}
            {...settings}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '100vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Classic Navigation
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import {
      PageContainer,
      ProCard,
      ProLayout,
      SettingDrawer,
    } from '@ant-design/pro-components';
    import { Button, Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const [settings, setSetting] = useState<Partial<ProSettings> | undefined>({
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      });
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            token={{
              header: {
                colorBgHeader: '#292f33',
                colorHeaderTitle: '#fff',
                colorTextMenu: '#dfdfdf',
                colorTextMenuSecondary: '#dfdfdf',
                colorTextMenuSelected: '#fff',
                colorBgMenuItemSelected: '#22272b',
                colorTextMenuActive: 'rgba(255,255,255,0.85)',
                colorTextRightActionsItem: '#dfdfdf',
              },
              colorTextAppListIconHover: '#fff',
              colorTextAppListIcon: '#dfdfdf',
              sider: {
                colorMenuBackground: '#fff',
                colorMenuItemDivider: '#dfdfdf',
                colorBgMenuItemHover: '#f6f6f6',
                colorTextMenu: '#595959',
                colorTextMenuSelected: '#242424',
                colorTextMenuActive: '#242424',
              },
            }}
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: (
                <div
                  style={{
                    color: '#dfdfdf',
                  }}
                >
                  书琰
                </div>
              ),
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                        backgroundColor: 'rgba(57,62,67,1)',
                        color: '#fff',
                      }}
                      prefix={
                        <SearchOutlined
                          style={{
                            color: '#dfdfdf',
                          }}
                        />
                      }
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <div
                  style={{
                    textAlign: 'center',
                    paddingBlockStart: 12,
                  }}
                >
                  <div>© 2021 Made with love</div>
                  <div>by Ant Design</div>
                </div>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
            {...settings}
          >
            <PageContainer
              breadcrumb={{
                routes: [],
              }}
              onBack={() => window.history.back()}
              extra={[
                <Button key="3">操作</Button>,
                <Button key="2">操作</Button>,
                <Button key="1" type="primary">
                  主操作
                </Button>,
              ]}
              footer={[
                <Button key="3">重置</Button>,
                <Button key="2" type="primary">
                  提交
                </Button>,
              ]}
            >
              <ProCard
                style={{
                  height: '200vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
          <SettingDrawer
            pathname={pathname}
            enableDarkTheme
            getContainer={() => document.getElementById('test-pro-layout')}
            settings={settings}
            onSettingChange={(changeSetting) => {
              setSetting(changeSetting);
            }}
            disableUrlParams={false}
          />
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Show Title When Collapsed
    import {
      CrownFilled,
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
      SmileFilled,
      TabletFilled,
    } from '@ant-design/icons';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Button, Input } from 'antd';
    import { useState } from 'react';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <ProLayout
          collapsed={true}
          collapsedButtonRender={false}
          layout="side"
          route={{
            routes: [
              {
                path: '/welcome',
                name: '欢迎',
                icon: <SmileFilled />,
                component: './Welcome',
              },
              {
                path: '/admin',
                name: '管理',
                icon: <CrownFilled />,
                access: 'canAdmin',
                component: './Admin',
              },
              {
                path: '/list',
                name: '列表',
                icon: <TabletFilled />,
                access: 'canAdmin',
                component: './Admin',
              },
            ],
          }}
          location={{
            pathname,
          }}
          menu={{
            type: 'group',
            collapsedShowTitle: true,
          }}
          avatarProps={{
            src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
            size: 'small',
            title: '书琰',
          }}
          actionsRender={(props) => {
            if (props.isMobile) return [];
            return [
              props.layout !== 'side' ? (
                <div
                  key="SearchOutlined"
                  aria-hidden
                  style={{
                    display: 'flex',
                    alignItems: 'center',
                    marginInlineEnd: 24,
                  }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    e.preventDefault();
                  }}
                >
                  <Input
                    style={{
                      borderRadius: 4,
                      marginInlineEnd: 12,
                      backgroundColor: 'rgba(0,0,0,0.03)',
                    }}
                    prefix={
                      <SearchOutlined
                        style={{
                          color: 'rgba(0, 0, 0, 0.15)',
                        }}
                      />
                    }
                    placeholder="搜索方案"
                    variant="borderless"
                  />
                  <PlusCircleFilled
                    style={{
                      color: 'var(--ant-primary-color)',
                      fontSize: 24,
                    }}
                  />
                </div>
              ) : undefined,
              <InfoCircleFilled key="InfoCircleFilled" />,
              <QuestionCircleFilled key="QuestionCircleFilled" />,
              <GithubFilled key="GithubFilled" />,
            ];
          }}
          menuFooterRender={(props) => {
            if (props?.collapsed) return undefined;
            return (
              <p
                style={{
                  textAlign: 'center',
                  paddingBlockStart: 12,
                }}
              >
                Power by Ant Design
              </p>
            );
          }}
          onMenuHeaderClick={demoOnMenuHeaderClick}
          menuItemRender={(item, dom) => (
            <a
              onClick={() => {
                setPathname(item.path || '/welcome');
              }}
            >
              {dom}
            </a>
          )}
        >
          <PageContainer
            extra={[
              <Button key="3">操作</Button>,
              <Button key="2">操作</Button>,
              <Button key="1" type="primary">
                主操作
              </Button>,
            ]}
            footer={[
              <Button key="3">重置</Button>,
              <Button key="2" type="primary">
                提交
              </Button>,
            ]}
          >
            <ProCard
              style={{
                height: '200vh',
                minHeight: 800,
              }}
            >
              <div />
            </ProCard>
          </PageContainer>
        </ProLayout>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Non-grouped Menu
    import {
      CrownOutlined,
      InfoCircleOutlined,
      MergeCellsOutlined,
      QuestionCircleOutlined,
      TabletOutlined,
      UserOutlined,
    } from '@ant-design/icons';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Button, Result } from 'antd';
    import { useState } from 'react';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/welcome');
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            fixSiderbar
            route={{
              path: '/',
              routes: [
                {
                  path: '/admin',
                  name: '管理页',
                  icon: <CrownOutlined />,
                  access: 'canAdmin',
                  component: './Admin',
                  routes: [
                    {
                      path: '/admin/sub-page1',
                      name: '一级页面',
                      icon: <CrownOutlined />,
                      component: './Welcome',
                    },
                    {
                      path: '/admin/sub-page2',
                      name: '二级页面',
                      icon: <CrownOutlined />,
                      component: './Welcome',
                    },
                    {
                      path: '/admin/sub-page3',
                      name: '三级页面',
                      icon: <CrownOutlined />,
                      component: './Welcome',
                    },
                  ],
                },
                {
                  name: '列表页',
                  icon: <TabletOutlined />,
                  path: '/list',
                  component: './ListTableList',
                  routes: [
                    {
                      path: '/list/sub-page2',
                      name: '二级列表页面',
                      icon: <CrownOutlined />,
                      component: './Welcome',
                    },
                    {
                      path: '/list/sub-page3',
                      name: '三级列表页面',
                      icon: <CrownOutlined />,
                      component: './Welcome',
                    },
                  ],
                },
              ],
            }}
            location={{
              pathname,
            }}
            waterMarkProps={{
              content: 'Pro Layout',
            }}
            avatarProps={{
              icon: <UserOutlined />,
              size: 'small',
              title: '书琰',
            }}
            actionsRender={() => [
              <InfoCircleOutlined key="InfoCircleOutlined" />,
              <QuestionCircleOutlined key="QuestionCircleOutlined" />,
              <MergeCellsOutlined key="MergeCellsOutlined" />,
            ]}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <p
                  style={{
                    textAlign: 'center',
                    color: 'rgba(0,0,0,0.6)',
                    paddingBlockStart: 12,
                  }}
                >
                  Power by Ant Design
                </p>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '120vh',
                  minHeight: 600,
                }}
              >
                <Result
                  status="404"
                  style={{
                    height: '100%',
                  }}
                  title="Hello World"
                  subTitle="Sorry, you are not authorized to access this page."
                  extra={<Button type="primary">Back Home</Button>}
                />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Search Menu
    import { PlusCircleFilled, SearchOutlined } from '@ant-design/icons';
    import type { MenuDataItem } from '@ant-design/pro-components';
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import { Input, Space } from 'antd';
    import { useState } from 'react';
    import complexMenu from './complex-menu';
    
    const filterByMenuData = (
      data: MenuDataItem[],
      keyWord: string,
    ): MenuDataItem[] =>
      data
        .map((item) => {
          if (item.name?.includes(keyWord)) {
            return { ...item };
          }
          const children = filterByMenuData(item.children || [], keyWord);
          if (children.length > 0) {
            return { ...item, children };
          }
          return undefined;
        })
        .filter((item) => item) as MenuDataItem[];
    
    const loopMenuItem = (menus: any[]): MenuDataItem[] =>
      menus.map(({ icon: _icon, routes, ...item }) => ({
        ...item,
        children: routes && loopMenuItem(routes),
      }));
    
    const Demo = () => {
      const [keyWord, setKeyWord] = useState('');
      return (
        <div
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            location={{
              pathname: '/home/overview',
            }}
            menuExtraRender={({ collapsed }) =>
              !collapsed && (
                <Space
                  style={{
                    marginBlockStart: 16,
                  }}
                  align="center"
                >
                  <Input
                    style={{
                      borderRadius: 4,
                      backgroundColor: 'rgba(0,0,0,0.03)',
                    }}
                    prefix={
                      <SearchOutlined
                        style={{
                          color: 'rgba(0, 0, 0, 0.15)',
                        }}
                      />
                    }
                    placeholder="搜索方案"
                    variant="borderless"
                    onPressEnter={(e) => {
                      setKeyWord((e.target as HTMLInputElement).value);
                    }}
                  />
                  <PlusCircleFilled
                    style={{
                      color: 'var(--ant-primary-color)',
                      fontSize: 24,
                    }}
                  />
                </Space>
              )
            }
            menuDataRender={() => loopMenuItem(complexMenu)}
            postMenuData={(menus) => filterByMenuData(menus || [], keyWord)}
          >
            <PageContainer content="欢迎使用">
              <div>Hello World</div>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Expand menu — site map
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const settings: ProSettings | undefined = {
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      };
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: '书琰',
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                        backgroundColor: 'rgba(0,0,0,0.03)',
                      }}
                      prefix={
                        <SearchOutlined
                          style={{
                            color: 'rgba(0, 0, 0, 0.15)',
                          }}
                        />
                      }
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <div
                  style={{
                    textAlign: 'center',
                    paddingBlockStart: 12,
                  }}
                >
                  <div>© 2021 Made with love</div>
                  <div>by Ant Design</div>
                </div>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <div
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </div>
            )}
            {...settings}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '100vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Multiple Routes for One Menu
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
        }}
      >
        <ProLayout
          location={{
            pathname: '/config/template/new',
          }}
          route={{
            routes: [
              {
                path: '/config',
                name: '配置中心',
                routes: [
                  {
                    path: 'product',
                    name: '产品配置',
                    indexRoute: {
                      component: 'ConfigProduct/index',
                    },
                    routes: [
                      {
                        path: 'new',
                        component: 'ConfigProduct/NewConfig',
                      },
                      {
                        path: 'edit/:productKey',
                        component: 'ConfigProduct/NewConfig',
                      },
                      {
                        path: 'detail/:productKey',
                        component: 'ConfigProduct/Detail',
                      },
                    ],
                  },
                  {
                    path: 'productManage',
                    name: '产品管理',
                    indexRoute: {
                      component: 'ConfigProductAll/index',
                    },
                    routes: [
                      {
                        path: 'detail/:productKey',
                        component: 'ConfigProductAll/Detail',
                      },
                    ],
                  },
                  {
                    path: 'template',
                    name: '产品模板管理',
                    indexRoute: {
                      component: 'ConfigTemplate/index',
                    },
                    routes: [
                      {
                        path: 'new',
                        component: 'ConfigTemplate/NewConfig',
                      },
                      {
                        path: 'edit/:templateKey',
                        component: 'ConfigTemplate/NewConfig',
                      },
                      {
                        path: 'detail/:templateKey',
                        component: 'ConfigTemplate/Detail',
                      },
                    ],
                  },
                  {
                    path: 'configItem',
                    name: '配置项模板管理',
                    indexRoute: {
                      component: 'ConfigItem/index',
                    },
                    routes: [
                      {
                        path: 'new',
                        component: 'ConfigItem/NewConfig',
                      },
                      {
                        path: 'edit/:productKey',
                        component: 'ConfigItem/NewConfig',
                      },
                      {
                        path: 'detail/:productKey',
                        component: 'ConfigItem/Detail',
                      },
                    ],
                  },
                  {
                    path: 'meta',
                    name: '元数据管理',
                    component: 'ConfigMeta',
                  },
                ],
              },
              {
                path: 'asset',
                name: '资产',
                routes: [
                  {
                    path: 'query',
                    name: '资产查询',
                    component: 'Asset',
                  },
                  {
                    path: 'collateral',
                    name: '抵押查询',
                    component: 'Collateral',
                  },
                ],
              },
              {
                path: 'bill',
                name: '账单',
                routes: [
                  {
                    path: 'billNo',
                    name: '账单编号',
                    component: 'BillNo',
                  },
                  {
                    path: 'bill',
                    name: '账单查询',
                    component: 'Bill',
                  },
                  {
                    path: 'billItem',
                    name: '账单条目',
                    component: 'BillItem',
                  },
                ],
              },
              {
                path: 'cif',
                name: 'CIF',
                routes: [
                  {
                    path: 'bankAccount',
                    name: '绑卡信息',
                    component: 'CifBankAccount',
                  },
                  {
                    path: 'userGroup',
                    name: '查询 Group',
                    component: 'CifUserGroup',
                  },
                  {
                    path: 'userId',
                    name: '查询 ID',
                    component: 'CifUserId',
                  },
                  {
                    path: 'newInstitution',
                    name: '新增机构',
                    indexRoute: {
                      component: 'CifNewInstitution/index',
                    },
                    routes: [
                      {
                        path: 'new',
                        component: 'CifNewInstitution/ApplyNew',
                      },
                      {
                        path: 'bind/:id',
                        component: 'CifNewInstitution/BindAccount',
                      },
                    ],
                  },
                ],
              },
              {
                path: 'tools',
                name: '小工具',
                routes: [
                  {
                    path: 'ttsql',
                    name: 'MySQL转BlinkTT流表',
                    component: 'ToolTT',
                  },
                ],
              },
            ],
          }}
        >
          <PageContainer content="欢迎使用">
            <div>Hello World</div>
          </PageContainer>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Open All Menus by Default

    Set defaultOpenAll on menu to expand every submenu on first render.

    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import complexMenu from './complex-menu';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
        }}
      >
        <ProLayout
          location={{
            pathname: '/data_hui/data_hui2',
          }}
          collapsed={false}
          collapsedButtonRender={false}
          route={{
            routes: complexMenu,
          }}
          menu={{ defaultOpenAll: true }}
        >
          <PageContainer content="欢迎使用">
            <div>Hello World</div>
          </PageContainer>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Always expand all submenus

    After toggling collapse repeatedly, defaultOpenAll may stop matching user intent. Set ignoreFlatMenu on menu to ignore manually collapsed state and keep the “expand all” behavior. This runs before render, so it does not apply to async menus.

    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
        }}
      >
        <ProLayout
          location={{
            pathname: '/data_hui/data_hui2',
          }}
          collapsed={false}
          collapsedButtonRender={false}
          route={{
            routes: [
              {
                path: '/home',
                name: '首页',
                locale: 'menu.home',
                routes: [
                  {
                    path: '/home/overview',
                    name: '概述',
                    hideInMenu: true,
                    locale: 'menu.home.overview',
                  },
                  {
                    path: '/home/search',
                    name: '搜索',
                    hideInMenu: true,
                    locale: 'menu.home.search',
                  },
                ],
              },
              {
                path: '/data_hui',
                name: '汇总数据',
                locale: 'menu.data_hui',
                routes: [
                  {
                    collapsed: true,
                    menuName: '域买家维度交易',
                    name: '域买家维度交易',
                    path: '/xx',
                    routes: [
                      {
                        id: 2,
                        name: '月表',
                        path: '/data_hui2',
                      },
                      {
                        name: '日表',
                        path: '/data_hui3?tableName=adm_rk_cr_tb_trv_byr_ds&tableSchema=alifin_odps_birisk',
                      },
                    ],
                  },
                  {
                    name: '维度交易',
                    path: '/',
                    routes: [
                      {
                        name: '月表',
                        path: '/data_hui4',
                      },
                      {
                        name: '日表',
                        key: 'tableName=adm_rk_cr_tb_trv_byr_ds&tableSchema=alifin_odps_birisk',
                        path: '/data_hui5',
                      },
                    ],
                  },
                ],
              },
            ],
          }}
          menu={{
            defaultOpenAll: true,
            ignoreFlatMenu: true,
          }}
        >
          <PageContainer content="欢迎使用">
            <div>Hello World</div>
          </PageContainer>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Load Menu from Server

    ProLayout provides a powerful menu, but this necessarily encapsulates a lot of behavior, leading to dissatisfaction for users who need some special logic. So we provide a number of APIs that are expected to satisfy the vast majority of our clients in this way.

    The main APIs used to load menu from the server are menuDataRender and menuRender, menuDataRender controls the current menu data and menuRender controls the menu's dom node.

    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import { Button, Switch } from 'antd';
    import { useRef, useState } from 'react';
    import customMenuDate from './custom-menu';
    
    const waitTime = (time: number = 100) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(true);
        }, time);
      });
    };
    
    let serviceData: any[] = customMenuDate;
    
    const Demo = () => {
      const actionRef = useRef<{
        reload: () => void;
      }>();
      const [toggle, setToggle] = useState(false);
      return (
        <>
          <ProLayout
            style={{
              height: '100vh',
            }}
            actionRef={actionRef}
            suppressSiderWhenMenuEmpty={toggle}
            menu={{
              request: async () => {
                await waitTime(2000);
                return serviceData;
              },
            }}
            location={{
              pathname: '/welcome/welcome',
            }}
          >
            <PageContainer content="欢迎使用">
              <div>
                当从服务器获取的菜单为空时隐藏Sider:
                <Switch checked={toggle} onChange={setToggle} />
              </div>
              Hello World
              <Button
                style={{
                  margin: 8,
                }}
                onClick={() => {
                  serviceData = customMenuDate;
                  actionRef.current?.reload();
                }}
              >
                刷新菜单
              </Button>
              <Button
                style={{
                  margin: 8,
                }}
                onClick={() => {
                  serviceData = [];
                  actionRef.current?.reload();
                }}
              >
                刷新菜单(空数据)
              </Button>
            </PageContainer>
          </ProLayout>
        </>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Customize Menu Content

    With menuItemRender, subMenuItemRender, title, logo, menuHeaderRender you can customize the menu style very easily. If you are really not satisfied, you can use menuRender to fully customize it.

    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      return (
        <div
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            menuItemRender={(item, dom) => (
              <div
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  gap: 8,
                }}
              >
                pre {dom}
              </div>
            )}
            subMenuItemRender={(_, dom) => (
              <div
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  gap: 8,
                }}
              >
                pre {dom}
              </div>
            )}
            title="Remax"
            logo="https://gw.alipayobjects.com/mdn/rms_b5fcc5/afts/img/A*1NHAQYduQiQAAAAAAAAAAABkARQnAQ"
            menuHeaderRender={(logo, title) => (
              <div
                id="customize_menu_header"
                style={{
                  height: '32px',
                  display: 'flex',
                  alignItems: 'center',
                  gap: 8,
                }}
                onClick={() => {
                  window.open('https://remaxjs.org/');
                }}
              >
                {logo}
                {title}
              </div>
            )}
            {...defaultProps}
            location={{
              pathname: '/welcome',
            }}
          >
            <PageContainer content="欢迎使用">Hello World</PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    ProLayout does not provide footer by default, if you want to have the same style as Pro official website, you need to introduce a footer by yourself.

    import {
      DefaultFooter,
      PageContainer,
      ProLayout,
    } from '@ant-design/pro-components';
    import defaultProps from './_defaultProps';
    
    const Demo = () => (
      <ProLayout
        {...defaultProps}
        style={{
          height: '100vh',
        }}
        breakpoint={false}
        collapsed
        location={{
          pathname: '/welcome',
        }}
        footerRender={() => (
          <DefaultFooter
            links={[
              {
                key: 'procomponents',
                title: 'ProComponents',
                href: 'https://procomponents.ant.design/',
              },
              {
                key: 'antd',
                title: 'Ant Design',
                href: 'https://ant.design/',
              },
            ]}
            copyright="ProComponents 页脚示例"
          />
        )}
      >
        <PageContainer content="欢迎使用">Hello World</PageContainer>
      </ProLayout>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Footer Toolbar & Announcements
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Alert, Button, Input, Space } from 'antd';
    import React, { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      const settings: ProSettings | undefined = {
        layout: 'mix',
        splitMenus: true,
      };
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <>
          <div
            id="test-pro-layout"
            style={{
              height: '100vh',
            }}
          >
            <ProLayout
              token={{
                header: {
                  heightLayoutHeader: 108,
                },
              }}
              headerRender={(props, defaultDom) => (
                <>
                  <Alert
                    title={
                      <div
                        style={{
                          color: 'white',
                        }}
                      >
                        本网站提供的部分服务在你当前浏览器中无法使用,建议你更换为
                        Chrome 浏览器查看本网站。
                      </div>
                    }
                    icon={
                      <InfoCircleFilled
                        style={{
                          color: 'white',
                        }}
                      />
                    }
                    banner
                    style={{
                      backgroundColor: 'black',
                    }}
                    action={
                      <Button
                        type="text"
                        style={{
                          color: 'white',
                        }}
                      >
                        查看详情
                      </Button>
                    }
                  />
                  {React.cloneElement(defaultDom as any, {
                    style: {
                      height: '56px',
                      lineHeight: '56px',
                    },
                  })}
                </>
              )}
              footerRender={() => (
                <Space
                  style={{
                    height: 64,
                    display: 'flex',
                    justifyContent: 'end',
                    alignItems: 'center',
                    marginInlineEnd: 24,
                  }}
                >
                  <Button key="1">上一步</Button>
                  <Button key="2" type="primary">
                    保存
                  </Button>
                </Space>
              )}
              bgLayoutImgList={[
                {
                  src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                  left: 85,
                  bottom: 100,
                  height: '303px',
                },
                {
                  src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                  bottom: -68,
                  right: -45,
                  height: '303px',
                },
                {
                  src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                  bottom: 0,
                  left: 0,
                  width: '331px',
                },
              ]}
              {...defaultProps}
              location={{
                pathname,
              }}
              menu={{
                type: 'group',
              }}
              avatarProps={{
                src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
                size: 'small',
                title: '书琰',
              }}
              actionsRender={(props) => {
                if (props.isMobile) return [];
                return [
                  props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                    <div
                      key="SearchOutlined"
                      aria-hidden
                      style={{
                        display: 'flex',
                        alignItems: 'center',
                        marginInlineEnd: 24,
                      }}
                      onMouseDown={(e) => {
                        e.stopPropagation();
                        e.preventDefault();
                      }}
                    >
                      <Input
                        style={{
                          borderRadius: 4,
                          marginInlineEnd: 12,
                          backgroundColor: 'rgba(0,0,0,0.03)',
                        }}
                        prefix={
                          <SearchOutlined
                            style={{
                              color: 'rgba(0, 0, 0, 0.15)',
                            }}
                          />
                        }
                        placeholder="搜索方案"
                        variant="borderless"
                      />
                      <PlusCircleFilled
                        style={{
                          color: 'var(--ant-primary-color)',
                          fontSize: 24,
                        }}
                      />
                    </div>
                  ) : undefined,
                  <InfoCircleFilled key="InfoCircleFilled" />,
                  <QuestionCircleFilled key="QuestionCircleFilled" />,
                  <GithubFilled key="GithubFilled" />,
                ];
              }}
              menuFooterRender={(props) => {
                if (props?.collapsed) return undefined;
                return (
                  <div
                    style={{
                      textAlign: 'center',
                      paddingBlockStart: 12,
                    }}
                  >
                    <div>© 2021 Made with love</div>
                    <div>by Ant Design</div>
                  </div>
                );
              }}
              menuItemRender={(item, dom) => (
                <div
                  onClick={() => {
                    setPathname(item.path || '/welcome');
                  }}
                >
                  {dom}
                </div>
              )}
              {...settings}
            >
              <PageContainer>
                <ProCard
                  style={{
                    minHeight: 800,
                  }}
                >
                  <div />
                </ProCard>
              </PageContainer>
            </ProLayout>
          </div>
        </>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Layout Nesting & Navigation

    Nested Layout
    import { UserOutlined } from '@ant-design/icons';
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import complexMenu from './complex-menu';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
        }}
      >
        <ProLayout
          location={{
            pathname: '/home',
          }}
          fixSiderbar={false}
          collapsedButtonRender={false}
          collapsed={false}
          iconfontUrl="//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js"
          route={{
            routes: [
              {
                path: '/home',
                name: '收藏',
                icon: 'icon-shoucang1',
              },
              {
                path: '/home/overview',
                name: 'FaceBook',
                icon: 'icon-facebook',
              },
              {
                path: '/home/search',
                name: 'Twitter',
                icon: 'icon-twitter',
              },
            ],
          }}
          headerRender={false}
        >
          <ProLayout
            location={{
              pathname: '/home/overview',
            }}
            fixSiderbar={false}
            route={{
              routes: complexMenu,
            }}
            style={{
              height: '400px',
            }}
            avatarProps={{
              icon: <UserOutlined />,
            }}
            menuHeaderRender={false}
          >
            <PageContainer content="欢迎使用">
              <div>Hello World</div>
            </PageContainer>
          </ProLayout>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Breadcrumb on Top
    import {
      PageContainer,
      ProBreadcrumb,
      ProLayout,
    } from '@ant-design/pro-components';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
        }}
      >
        <ProLayout
          location={{
            pathname: '/admin/process/edit/123',
          }}
          layout="mix"
          ErrorBoundary={false}
          headerContentRender={() => <ProBreadcrumb />}
          breadcrumbRender={(routers = []) => [
            {
              path: '/',
              title: '主页',
            },
            {
              path: '/',
              title: '测试页',
            },
            ...routers,
          ]}
          menuDataRender={() => [
            {
              path: '/welcome',
              name: '欢迎',
            },
            {
              path: '/admin',
              name: '管理',
              children: [
                {
                  name: '申请单列表',
                  path: '/admin/process',
                },
                {
                  name: '申请单详情',
                  path: '/admin/process/detail/:id',
                  hideInMenu: true,
                },
                {
                  name: '编辑申请单',
                  path: '/admin/process/edit/:id',
                  hideInMenu: true,
                },
                {
                  name: '添加申请单',
                  path: '/admin/process/add',
                  hideInMenu: true,
                },
              ],
            },
          ]}
        >
          <PageContainer content="欢迎使用" breadcrumbRender={false}>
            <div>Hello World</div>
          </PageContainer>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Multi-level Navigation
    import {
      CrownOutlined,
      LeftOutlined,
      SmileOutlined,
      UserOutlined,
    } from '@ant-design/icons';
    import { ProLayout } from '@ant-design/pro-components';
    import { Button, Result, Space, Tag } from 'antd';
    import { useState } from 'react';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const defaultProps = {
      routes: [
        {
          path: '/admin/sub-page1',
          name: '一级页面',
          icon: <CrownOutlined />,
          component: './Welcome',
        },
        {
          path: '/admin/sub-page2',
          name: '二级页面',
          icon: <UserOutlined />,
          component: './Welcome',
        },
        {
          path: '/admin/sub-page3',
          name: '三级页面',
          icon: <SmileOutlined />,
          component: './Welcome',
        },
      ],
    };
    
    const defaultHomeProps = {
      routes: [
        {
          path: '/',
          name: '首页',
          icon: <CrownOutlined />,
          component: './Welcome',
        },
        {
          path: '/admin/sub-page1',
          name: '详情页',
          icon: <SmileOutlined />,
          component: './Welcome',
        },
      ],
    };
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/admin/sub-page1');
      return (
        <>
          <ProLayout
            route={pathname === '/' ? defaultHomeProps : defaultProps}
            location={{
              pathname,
            }}
            logoStyle={{
              backgroundColor: '#eee',
            }}
            menuHeaderRender={(logo, title, props) => {
              if (pathname === '/') {
                return (
                  <a>
                    {logo}
                    {title}
                  </a>
                );
              }
              if (props?.collapsed) {
                return (
                  <Space>
                    <LeftOutlined
                      style={{
                        fontSize: 24,
                      }}
                    />
                  </Space>
                );
              }
              return (
                <Space orientation="vertical">
                  <Button
                    size="small"
                    icon={<LeftOutlined />}
                    onClick={() => {
                      setPathname('/');
                    }}
                  >
                    返回应用列表
                  </Button>
                  <b>alipay_dev_gzone</b>
                  <span>Creation Time 2017-01-10</span>
                  <Tag color="warning">运行中</Tag>
                </Space>
              );
            }}
            fixSiderbar
            headerRender={() => {
              return null;
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
            avatarProps={{
              icon: <UserOutlined />,
            }}
          >
            <div
              style={{
                height: '120vh',
                minHeight: 600,
              }}
            >
              <Result
                status="404"
                style={{
                  height: '100%',
                  background: '#fff',
                }}
                icon={
                  <img
                    src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*aPIBS5gRPu4AAAAAAAAAAAAAARQnAQ"
                    alt="404"
                  />
                }
                title="Hello World"
                subTitle="Sorry, you are not authorized to access this page."
                extra={<Button type="primary">Back Home</Button>}
              />
            </div>
          </ProLayout>
        </>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Immersive Navigation
    import { CrownOutlined, SmileOutlined, UserOutlined } from '@ant-design/icons';
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import { Button, Input, Result, Tag } from 'antd';
    import { useState } from 'react';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const defaultProps = {
      routes: [
        {
          path: '/welcome',
          name: '欢迎',
          icon: <CrownOutlined />,
          component: './Welcome',
        },
        {
          path: '/admin/sub-page2',
          name: '二级页面',
          icon: <UserOutlined />,
          component: './Welcome',
        },
        {
          path: '/admin/sub-page3',
          name: '三级页面',
          icon: <SmileOutlined />,
          component: './Welcome',
        },
      ],
    };
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/welcome');
      return (
        <>
          <ProLayout
            route={defaultProps}
            location={{
              pathname,
            }}
            fixSiderbar
            headerRender={false}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
            avatarProps={{
              icon: <UserOutlined />,
            }}
          >
            <PageContainer
              onBack={() => null}
              tags={<Tag color="blue">状态一</Tag>}
              header={{
                style: {
                  padding: '8px 16px',
                  backgroundColor: '#fff',
                  position: 'fixed',
                  top: 0,
                  width: '100%',
                  left: 0,
                  zIndex: 999,
                  boxShadow: '0 2px 8px #f0f1f2',
                },
              }}
              style={{
                paddingBlockStart: 48,
              }}
              extra={[
                <Input.Search
                  key="search"
                  style={{
                    width: 240,
                  }}
                />,
                <Button key="3">操作一</Button>,
                <Button key="2" type="primary">
                  操作一
                </Button>,
              ]}
            >
              <div
                style={{
                  height: '120vh',
                  minHeight: 600,
                }}
              >
                <Result
                  status="404"
                  style={{
                    height: '100%',
                    background: '#fff',
                  }}
                  title="Hello World"
                  subTitle="Sorry, you are not authorized to access this page."
                  extra={<Button type="primary">Back Home</Button>}
                />
              </div>
            </PageContainer>
          </ProLayout>
        </>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Cross-site navigation

    For the default card layout, each item should have desc set to a truthy value. For grouped layout, each item should include children with length greater than 0.

    App list — grouped mode
    import type { AppItemProps, ProSettings } from '@ant-design/pro-components';
    import { ProConfigProvider, ProLayout } from '@ant-design/pro-components';
    import { Modal } from 'antd';
    import defaultProps from './_defaultProps';
    
    const AppGroupList: any = [
      {
        title: 'UI 设计语言',
        desc: '杭州市较知名的 UI 设计语言',
        children: [
          {
            icon: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
            title: 'Ant Design',
            desc: '杭州市较知名的 UI 设计语言',
            url: 'https://ant.design',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
            title: 'Pro Components',
            desc: '专业级 UI 组件库',
            url: 'https://procomponents.ant.design/',
          },
        ],
      },
      {
        title: 'UI 设计语言 2组111',
        icon: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
        desc: '专业级 UI 组件库',
        url: 'https://procomponents.ant.design/',
        children: [
          {
            icon: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
            title: 'AntV',
            desc: '蚂蚁集团全新一代数据可视化解决方案',
            url: 'https://antv.vision/',
            target: '_blank',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
            title: 'AntV',
            desc: '蚂蚁集团全新一代数据可视化解决方案',
            url: 'https://antv.vision/',
            target: '_blank',
          },
        ],
      },
      {
        title: '待分组',
        desc: '专业级 UI 组件库',
        children: [
          {
            title: '工具',
            desc: '知识创作与分享工具',
            icon: 'https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg',
            url: 'https://www.yuque.com/',
          },
          {
            title: '前端应用框架',
            desc: '插件化的企业级前端应用框架。',
            icon: 'https://img.alicdn.com/tfs/TB1zomHwxv1gK0jSZFFXXb0sXXa-200-200.png',
            url: 'https://umijs.org/zh-CN/docs',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/bmw-prod/8a74c1d3-16f3-4719-be63-15e467a68a24/km0cv8vn_w500_h500.png',
            title: 'qiankun',
            desc: '可能是你见过最完善的微前端解决方案🧐',
            url: 'https://qiankun.umijs.org/',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/rmsportal/LFooOLwmxGLsltmUjTAP.svg',
            title: 'Kitchen ',
            desc: 'Sketch 工具集',
            url: 'https://kitchen.alipay.com/',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png',
            title: 'dumi',
            desc: '为组件开发场景而生的文档工具',
            url: 'https://d.umijs.org/zh-CN',
          },
        ],
      },
    ];
    
    const Demo = () => {
      const settings: ProSettings = {
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      };
      const itemClick = (
        item: AppItemProps,
        popoverRef?: React.RefObject<HTMLSpanElement>,
      ) => {
        // 点击后关闭 Popover
        popoverRef?.current?.click?.();
    
        Modal?.confirm({
          width: 600,
          title: '点击项 详细数据',
          content: (
            <pre style={{ overflow: 'auto' }}>
              {JSON.stringify(typeof item === 'object' ? item : {}, null, 2)}
            </pre>
          ),
          okText: '前往',
          onOk: () => window.open(item?.url),
        });
      };
    
      return (
        <div id="test-pro-layout" style={{ height: '100vh' }}>
          <ProConfigProvider hashed={false}>
            <ProLayout
              {...defaultProps}
              appList={AppGroupList}
              itemClick={itemClick}
              location={{ pathname: '/list/sub-page/sub-sub-page1' }}
              siderMenuType="group"
              menu={{ collapsedShowGroupTitle: true }}
              avatarProps={{
                src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
                size: 'small',
                title: '书琰',
              }}
              {...settings}
            />
          </ProConfigProvider>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Theme & Style Customization

    Modify Style via Token
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Button, Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <ProLayout
          splitMenus
          token={{
            colorBgAppListIconHover: 'rgba(0,0,0,0.06)',
            colorTextAppListIconHover: 'rgba(255,255,255,0.95)',
            colorTextAppListIcon: 'rgba(255,255,255,0.85)',
            sider: {
              colorBgCollapsedButton: '#fff',
              colorTextCollapsedButtonHover: 'rgba(0,0,0,0.65)',
              colorTextCollapsedButton: 'rgba(0,0,0,0.45)',
              colorMenuBackground: '#004FD9',
              colorBgMenuItemCollapsedElevated: 'rgba(0,0,0,0.85)',
              colorMenuItemDivider: 'rgba(255,255,255,0.15)',
              colorBgMenuItemHover: 'rgba(0,0,0,0.06)',
              colorBgMenuItemSelected: 'rgba(0,0,0,0.15)',
              colorTextMenuSelected: '#fff',
              colorTextMenuItemHover: 'rgba(255,255,255,0.75)',
              colorTextMenu: 'rgba(255,255,255,0.75)',
              colorTextMenuSecondary: 'rgba(255,255,255,0.65)',
              colorTextMenuTitle: 'rgba(255,255,255,0.95)',
              colorTextMenuActive: 'rgba(255,255,255,0.95)',
              colorTextSubMenuSelected: '#fff',
            },
            header: {
              colorBgHeader: '#004FD9',
              colorBgRightActionsItemHover: 'rgba(0,0,0,0.06)',
              colorTextRightActionsItem: 'rgba(255,255,255,0.65)',
              colorHeaderTitle: '#fff',
              colorBgMenuItemHover: 'rgba(0,0,0,0.06)',
              colorBgMenuItemSelected: 'rgba(0,0,0,0.15)',
              colorTextMenuSelected: '#fff',
              colorTextMenu: 'rgba(255,255,255,0.75)',
              colorTextMenuSecondary: 'rgba(255,255,255,0.65)',
              colorTextMenuActive: 'rgba(255,255,255,0.95)',
            },
          }}
          {...defaultProps}
          location={{
            pathname,
          }}
          avatarProps={{
            src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
            size: 'small',
            title: '书琰',
          }}
          actionsRender={(props) => {
            if (props.isMobile) return [];
            return [
              props.layout !== 'side' ? (
                <div
                  key="SearchOutlined"
                  aria-hidden
                  style={{
                    display: 'flex',
                    alignItems: 'center',
                    marginInlineEnd: 24,
                  }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    e.preventDefault();
                  }}
                >
                  <Input
                    style={{
                      borderRadius: 4,
                      marginInlineEnd: 12,
                      backgroundColor: 'rgba(0,0,0,0.03)',
                    }}
                    prefix={
                      <SearchOutlined
                        style={{
                          color: 'rgba(0, 0, 0, 0.15)',
                        }}
                      />
                    }
                    placeholder="搜索方案"
                    variant="borderless"
                  />
                  <PlusCircleFilled
                    style={{
                      color: 'var(--ant-primary-color)',
                      fontSize: 24,
                    }}
                  />
                </div>
              ) : undefined,
              <InfoCircleFilled key="InfoCircleFilled" />,
              <QuestionCircleFilled key="QuestionCircleFilled" />,
              <GithubFilled key="GithubFilled" />,
            ];
          }}
          menuFooterRender={(props) => {
            if (props?.collapsed) return undefined;
            return (
              <p
                style={{
                  textAlign: 'center',
                  paddingBlockStart: 12,
                }}
              >
                Power by Ant Design
              </p>
            );
          }}
          onMenuHeaderClick={demoOnMenuHeaderClick}
          menuItemRender={(item, dom) => (
            <a
              onClick={() => {
                setPathname(item.path || '/welcome');
              }}
            >
              {dom}
            </a>
          )}
          layout="top"
        >
          <PageContainer
            extra={[
              <Button key="3">操作</Button>,
              <Button key="2">操作</Button>,
              <Button key="1" type="primary">
                主操作
              </Button>,
            ]}
            footer={[
              <Button key="3">重置</Button>,
              <Button key="2" type="primary">
                提交
              </Button>,
            ]}
          >
            <ProCard
              style={{
                height: '200vh',
                minHeight: 800,
              }}
            >
              <div />
            </ProCard>
          </PageContainer>
        </ProLayout>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Dark Theme
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import {
      PageContainer,
      ProCard,
      ProConfigProvider,
      ProLayout,
    } from '@ant-design/pro-components';
    import { Button, Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <ProConfigProvider dark={true}>
          <ProLayout
            {...defaultProps}
            splitMenus
            location={{
              pathname,
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: '书琰',
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                        backgroundColor: 'rgba(0,0,0,0.03)',
                      }}
                      prefix={
                        <SearchOutlined
                          style={{
                            color: 'rgba(0, 0, 0, 0.15)',
                          }}
                        />
                      }
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <p
                  style={{
                    textAlign: 'center',
                    paddingBlockStart: 12,
                  }}
                >
                  Power by Ant Design
                </p>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
          >
            <PageContainer
              extra={[
                <Button key="3">操作</Button>,
                <Button key="2">操作</Button>,
                <Button key="1" type="primary">
                  主操作
                </Button>,
              ]}
              footer={[
                <Button key="3">重置</Button>,
                <Button key="2" type="primary">
                  提交
                </Button>,
              ]}
            >
              <ProCard
                style={{
                  height: '200vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </ProConfigProvider>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Page background and overall mood
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const settings: Partial<ProSettings> | undefined = {
        fixSiderbar: true,
        layout: 'top',
        splitMenus: true,
      };
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: <div>书琰</div>,
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                      }}
                      prefix={<SearchOutlined />}
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <div
                  style={{
                    textAlign: 'center',
                    paddingBlockStart: 12,
                  }}
                >
                  <div>© 2021 Made with love</div>
                  <div>by Ant Design</div>
                </div>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
            {...settings}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '200vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Customize Menu Style
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import {
      PageContainer,
      ProCard,
      ProLayout,
      SettingDrawer,
    } from '@ant-design/pro-components';
    import { Button, Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const [settings, setSettings] = useState<Partial<ProSettings> | undefined>({
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      });
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            token={{
              header: {
                colorBgHeader: '#292f33',
                colorHeaderTitle: '#fff',
                colorTextMenu: '#dfdfdf',
                colorTextMenuSecondary: '#dfdfdf',
                colorTextMenuSelected: '#fff',
                colorBgMenuItemSelected: '#22272b',
                colorTextRightActionsItem: '#dfdfdf',
              },
              sider: {
                colorMenuBackground: '#fff',
                colorMenuItemDivider: '#dfdfdf',
                colorTextMenu: '#595959',
                colorTextMenuSelected: 'rgba(42,122,251,1)',
                colorBgMenuItemSelected: 'rgba(230,243,254,1)',
              },
            }}
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: (
                <div
                  style={{
                    color: '#dfdfdf',
                  }}
                >
                  书琰
                </div>
              ),
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                        backgroundColor: 'rgba(57,62,67,1)',
                        color: '#fff',
                      }}
                      prefix={
                        <SearchOutlined
                          style={{
                            color: '#dfdfdf',
                          }}
                        />
                      }
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <div
                  style={{
                    textAlign: 'center',
                    paddingBlockStart: 12,
                  }}
                >
                  <div>© 2021 Made with love</div>
                  <div>by Ant Design</div>
                </div>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
            {...settings}
          >
            <PageContainer
              extra={[
                <Button key="3">操作</Button>,
                <Button key="2">操作</Button>,
                <Button key="1" type="primary">
                  主操作
                </Button>,
              ]}
              footer={[
                <Button key="3">重置</Button>,
                <Button key="2" type="primary">
                  提交
                </Button>,
              ]}
            >
              <ProCard
                style={{
                  height: '200vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
          <SettingDrawer
            pathname={pathname}
            enableDarkTheme
            getContainer={() => document.getElementById('test-pro-layout')}
            settings={settings}
            onSettingChange={(changeSetting) => {
              setSettings(changeSetting);
            }}
            disableUrlParams={false}
          />
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Sider width 256px
    import {
      GithubFilled,
      InfoCircleFilled,
      QuestionCircleFilled,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import {
      PageContainer,
      ProCard,
      ProLayout,
      SettingDrawer,
    } from '@ant-design/pro-components';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      const [settings, setSettings] = useState<Partial<ProSettings> | undefined>({
        layout: 'side',
      });
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            siderWidth={256}
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              title: '书琰',
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuItemRender={(item, dom) => (
              <div
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </div>
            )}
            {...settings}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '100vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
          <SettingDrawer
            pathname={pathname}
            enableDarkTheme
            getContainer={() => document.getElementById('test-pro-layout')}
            settings={settings}
            onSettingChange={(changeSetting) => {
              setSettings(changeSetting);
            }}
            disableUrlParams={false}
          />
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Simplify hierarchy via page background and cards
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const settings: Partial<ProSettings> | undefined = {
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      };
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            token={{
              bgLayout: '#fff',
              header: {
                colorBgHeader: '#fff',
              },
              sider: {
                colorMenuBackground: '#fff',
              },
              pageContainer: {
                colorBgPageContainer: '#fff',
              },
            }}
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            menu={{
              type: 'group',
            }}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
              title: <div>书琰</div>,
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                props.layout !== 'side' && document.body.clientWidth > 1400 ? (
                  <div
                    key="SearchOutlined"
                    aria-hidden
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      marginInlineEnd: 24,
                    }}
                    onMouseDown={(e) => {
                      e.stopPropagation();
                      e.preventDefault();
                    }}
                  >
                    <Input
                      style={{
                        borderRadius: 4,
                        marginInlineEnd: 12,
                      }}
                      prefix={<SearchOutlined />}
                      placeholder="搜索方案"
                      variant="borderless"
                    />
                    <PlusCircleFilled
                      style={{
                        color: 'var(--ant-primary-color)',
                        fontSize: 24,
                      }}
                    />
                  </div>
                ) : undefined,
                <InfoCircleFilled key="InfoCircleFilled" />,
                <QuestionCircleFilled key="QuestionCircleFilled" />,
                <GithubFilled key="GithubFilled" />,
              ];
            }}
            menuFooterRender={(props) => {
              if (props?.collapsed) return undefined;
              return (
                <div
                  style={{
                    textAlign: 'center',
                    paddingBlockStart: 12,
                  }}
                >
                  <div>© 2021 Made with love</div>
                  <div>by Ant Design</div>
                </div>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
            {...settings}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '200vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Extra demos

    Examples that were previously not wired in the docs: ConfigProvider prefix, breadcrumbRender, menuRender, grouped appList, nested top layout, etc.

    ConfigProvider prefixCls + ProLayout
    import { LikeOutlined, UserOutlined } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import {
      PageContainer,
      ProLayout,
      SettingDrawer,
    } from '@ant-design/pro-components';
    import {
      Button,
      ConfigProvider,
      Descriptions,
      Result,
      Space,
      Statistic,
    } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const content = (
      <Descriptions size="small" column={2}>
        <Descriptions.Item label="创建人">书琰</Descriptions.Item>
        <Descriptions.Item label="联系方式">
          <span>421421</span>
        </Descriptions.Item>
        <Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
        <Descriptions.Item label="更新时间">2017-10-10</Descriptions.Item>
        <Descriptions.Item label="备注">
          中国浙江省杭州市西湖区古翠路
        </Descriptions.Item>
      </Descriptions>
    );
    
    const Demo = () => {
      const [settings, setSettings] = useState<Partial<ProSettings> | undefined>(
        undefined,
      );
      const [pathname, setPathname] = useState('/welcome');
      return (
        <ConfigProvider prefixCls="qixian">
          <ProLayout
            {...defaultProps}
            style={{
              maxHeight: '100vh',
            }}
            location={{
              pathname,
            }}
            menuFooterRender={(props) => {
              return (
                <a
                  style={{
                    lineHeight: '48rpx',
                    display: 'flex',
                    height: 48,
                    color: 'rgba(255, 255, 255, 0.65)',
                    alignItems: 'center',
                  }}
                  href="https://preview.pro.ant.design/dashboard/analysis"
                  target="_blank"
                  rel="noreferrer"
                >
                  <img
                    alt="pro-logo"
                    src="https://procomponents.ant.design/favicon.ico"
                    style={{
                      width: 16,
                      height: 16,
                      margin: '0 16px',
                      marginInlineEnd: 10,
                    }}
                  />
                  {!props?.collapsed && 'Preview Pro'}
                </a>
              );
            }}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            menuItemRender={(item, dom) => (
              <div
                role="button"
                tabIndex={0}
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
                onKeyDown={(e) => {
                  if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    setPathname(item.path || '/welcome');
                  }
                }}
              >
                {dom}
              </div>
            )}
            avatarProps={{
              icon: <UserOutlined />,
            }}
            {...settings}
          >
            <PageContainer
              content={content}
              tabList={[
                {
                  tab: '基本信息',
                  key: 'base',
                },
                {
                  tab: '详细信息',
                  key: 'info',
                },
              ]}
              extraContent={
                <Space size={24}>
                  <Statistic
                    title="Feedback"
                    value={1128}
                    prefix={<LikeOutlined />}
                  />
                  <Statistic title="Unmerged" value={93} suffix="/ 100" />
                </Space>
              }
              extra={[
                <Button key="3">操作</Button>,
                <Button key="2">操作</Button>,
                <Button key="1" type="primary">
                  主操作
                </Button>,
              ]}
              footer={[
                <Button key="3">重置</Button>,
                <Button key="2" type="primary">
                  提交
                </Button>,
              ]}
            >
              <div
                style={{
                  height: '120vh',
                  minHeight: 600,
                }}
              >
                <Result
                  status="404"
                  style={{
                    height: '100%',
                    background: '#fff',
                  }}
                  title="Hello World"
                  subTitle="Sorry, you are not authorized to access this page."
                  extra={<Button type="primary">Back Home</Button>}
                />
              </div>
            </PageContainer>
          </ProLayout>
          <SettingDrawer
            getContainer={() => document.getElementById('test-pro-layout')}
            settings={settings}
            onSettingChange={(changeSetting) => setSettings(changeSetting)}
          />
        </ConfigProvider>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Grouped menu (complex-menu.ts + menu.type=group)
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import complexMenu from './complex-menu';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
        }}
      >
        <ProLayout
          location={{
            pathname: '/data_hui/data_hui2',
          }}
          route={{
            routes: complexMenu,
          }}
          menu={{ type: 'group' }}
        >
          <PageContainer content="欢迎使用">
            <div>Hello World</div>
          </PageContainer>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Custom menuRender (sider container width)
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import defaultProps from './_defaultProps';
    
    const Demo = () => (
      <ProLayout
        {...defaultProps}
        style={{
          height: '100vh',
        }}
        location={{
          pathname: '/welcome',
        }}
        menuRender={(props, dom) => (
          <div
            style={{
              background: '#fff',
              boxShadow: '2px 0 6px rgba(0, 21, 41, 0.35)',
              overflow: 'hidden',
              height: '100%',
              width: props.collapsed ? 0 : props.siderWidth || 256,
            }}
          >
            {dom}
          </div>
        )}
      >
        <PageContainer content="欢迎使用">Hello World</PageContainer>
      </ProLayout>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Top layout nested inner ProLayout
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import complexMenu from './complex-menu';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
          overflow: 'auto',
        }}
      >
        <ProLayout
          location={{
            pathname: '/articles/new',
          }}
          route={{
            routes: complexMenu,
          }}
          layout="top"
        >
          <ProLayout
            location={{
              pathname: '/home/overview',
            }}
            route={{
              routes: complexMenu,
            }}
            menuHeaderRender={false}
          >
            <PageContainer content="欢迎使用">
              <div>Hello World</div>
            </PageContainer>
          </ProLayout>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    breadcrumbRender: prepend home
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    
    const Demo = () => (
      <div
        style={{
          height: '100vh',
        }}
      >
        <ProLayout
          location={{
            pathname: '/admin/process/edit/123',
          }}
          breadcrumbRender={(routes) => [
            {
              path: '/',
              title: '主页',
            },
            ...(routes || []),
          ]}
          menuDataRender={() => [
            {
              path: '/welcome',
              name: '欢迎',
            },
            {
              path: '/admin',
              name: '管理',
              children: [
                {
                  name: '申请单列表',
                  path: '/admin/process',
                },
                {
                  name: '申请单详情',
                  path: '/admin/process/detail/:id',
                  hideInMenu: true,
                },
                {
                  name: '编辑申请单',
                  path: '/admin/process/edit/:id',
                  hideInMenu: true,
                },
                {
                  name: '添加申请单',
                  path: '/admin/process/add',
                  hideInMenu: true,
                },
              ],
            },
          ]}
        >
          <PageContainer content="欢迎使用">
            <div>Hello World</div>
          </PageContainer>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    App list — grouped (minimal data)
    import type { ProSettings } from '@ant-design/pro-components';
    import { ProConfigProvider, ProLayout } from '@ant-design/pro-components';
    import defaultProps from './_defaultProps';
    
    const AppGroupList: any = [
      {
        title: 'UI 设计语言',
        children: [
          {
            icon: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
            title: '应用内跳转',
            url: '/components/page-container',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
            title: 'Ant Design',
            url: 'https://ant.design',
          },
          {
            icon: () => (
              <img src="https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg" alt="" />
            ),
            title: 'Pro Components',
            url: 'https://procomponents.ant.design/',
          },
        ],
      },
      {
        title: 'UI 设计语言 2组111',
        icon: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
        url: 'https://procomponents.ant.design/',
        children: [
          {
            icon: 'W',
            title: 'AntV',
            url: 'https://antv.vision/',
            target: '_blank',
          },
          {
            title: 'AntV',
            url: 'https://antv.vision/',
            target: '_blank',
          },
        ],
      },
      {
        title: '待分组',
        children: [
          {
            title: '工具',
            icon: 'https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg',
            url: 'https://www.yuque.com/',
          },
          {
            title: '前端应用框架',
            icon: 'https://img.alicdn.com/tfs/TB1zomHwxv1gK0jSZFFXXb0sXXa-200-200.png',
            url: 'https://umijs.org/zh-CN/docs',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/bmw-prod/8a74c1d3-16f3-4719-be63-15e467a68a24/km0cv8vn_w500_h500.png',
            title: 'qiankun',
            url: 'https://qiankun.umijs.org/',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/rmsportal/LFooOLwmxGLsltmUjTAP.svg',
            title: 'Kitchen ',
            url: 'https://kitchen.alipay.com/',
          },
          {
            icon: 'https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png',
            title: 'dumi',
            url: 'https://d.umijs.org/zh-CN',
          },
        ],
      },
    ];
    
    const Demo = () => {
      const settings: ProSettings = {
        fixSiderbar: true,
        layout: 'mix',
        splitMenus: true,
      };
    
      return (
        <div id="test-pro-layout" style={{ height: '100vh' }}>
          <ProConfigProvider hashed={false}>
            <ProLayout
              {...defaultProps}
              appList={AppGroupList}
              location={{ pathname: '/list/sub-page/sub-sub-page1' }}
              siderMenuType="group"
              menu={{ collapsedShowGroupTitle: true }}
              avatarProps={{
                src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
                size: 'small',
                title: '书琰',
              }}
              {...settings}
            />
          </ProConfigProvider>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Others

    Customized Collapse
    import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
    import type { ProLayoutProps } from '@ant-design/pro-components';
    import {
      PageContainer,
      ProFormRadio,
      ProLayout,
    } from '@ant-design/pro-components';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/welcome');
      const [collapsed, setCollapsed] = useState(true);
      const [position, setPosition] = useState<'header' | 'menu'>('header');
      const children = (
        <PageContainer>
          <div
            style={{
              height: '120vh',
              minHeight: 600,
            }}
          >
            <ProFormRadio.Group
              label="按钮的位置"
              options={['header', 'menu'].map((value) => ({
                label: value,
                value,
              }))}
              fieldProps={{
                value: position,
                onChange: (e) => setPosition(e.target.value),
              }}
            />
          </div>
        </PageContainer>
      );
      const props: ProLayoutProps = {
        ...defaultProps,
        location: {
          pathname,
        },
        collapsed,
        fixSiderbar: true,
        collapsedButtonRender: false,
        menuItemRender: (item, dom) => (
          <a
            onClick={() => {
              setPathname(item.path || '/welcome');
            }}
          >
            {dom}
          </a>
        ),
      };
      if (position === 'menu') {
        return (
          <ProLayout
            {...props}
            layout="mix"
            onCollapse={setCollapsed}
            postMenuData={(menuData) => {
              return [
                {
                  icon: collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />,
                  name: ' ',
                  onTitleClick: () => setCollapsed(!collapsed),
                },
                ...(menuData || []),
              ];
            }}
          >
            {children}
          </ProLayout>
        );
      }
      return (
        <ProLayout
          {...props}
          layout="mix"
          onCollapse={setCollapsed}
          headerContentRender={() => {
            return (
              <div
                onClick={() => setCollapsed(!collapsed)}
                style={{
                  cursor: 'pointer',
                  fontSize: '16px',
                }}
              >
                {collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
              </div>
            );
          }}
        >
          {children}
        </ProLayout>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Custom area below sider menu
    import {
      GithubFilled,
      InfoCircleFilled,
      QuestionCircleFilled,
    } from '@ant-design/icons';
    import type { ProSettings } from '@ant-design/pro-components';
    import {
      PageContainer,
      ProCard,
      ProLayout,
      SettingDrawer,
    } from '@ant-design/pro-components';
    import { Avatar, Image, Space } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    
    const Demo = () => {
      const [settings, setSettings] = useState<Partial<ProSettings> | undefined>({
        layout: 'side',
      });
    
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            bgLayoutImgList={[
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                left: 85,
                bottom: 100,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
                bottom: -68,
                right: -45,
                height: '303px',
              },
              {
                src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
                bottom: 0,
                left: 0,
                width: '331px',
              },
            ]}
            {...defaultProps}
            location={{
              pathname,
            }}
            collapsed={false}
            menu={{
              type: 'group',
            }}
            actionsRender={(props) => {
              if (props.isMobile) return [];
              return [
                <div
                  key={1}
                  style={{
                    height: '200px',
                  }}
                >
                  <Image
                    width={'100%'}
                    preview={false}
                    height={132}
                    src="https://gw.alipayobjects.com/zos/bmw-prod/d283f09a-64d6-4d59-bfc7-37b49ea0da2b.svg"
                  />
                  <Space
                    align="center"
                    size="middle"
                    style={{
                      width: '100%',
                      marginBlockStart: '32px',
                    }}
                  >
                    <Avatar
                      src="https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg"
                      size="small"
                    />
                    <div
                      style={{
                        fontSize: '14px',
                        marginInlineEnd: '32px',
                      }}
                    >
                      书琰
                    </div>
                    <InfoCircleFilled key="InfoCircleFilled" />
                    <QuestionCircleFilled key="QuestionCircleFilled" />
                    <GithubFilled key="GithubFilled" />
                  </Space>
                </div>,
              ];
            }}
            menuRender={(_, defaultDom) => defaultDom}
            menuItemRender={(item, dom) => (
              <div
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </div>
            )}
            {...settings}
          >
            <PageContainer>
              <ProCard
                style={{
                  height: '100vh',
                  minHeight: 800,
                }}
              >
                <div />
              </ProCard>
            </PageContainer>
          </ProLayout>
          <SettingDrawer
            pathname={pathname}
            enableDarkTheme
            getContainer={() => document.getElementById('test-pro-layout')}
            settings={settings}
            onSettingChange={(changeSetting) => {
              setSettings(changeSetting);
            }}
            disableUrlParams={false}
          />
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Menu icons (SVG / React)
    import {
      FacebookOutlined,
      StarFilled,
      TwitterOutlined,
    } from '@ant-design/icons';
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    
    /**
     * 菜单 `icon` 支持 React 节点(含 @ant-design/icons)或图片/SVG URL 字符串。
     *
     * icon 选型说明:
     * - `StarFilled`:通用动作类图标,有官方 Filled 版,统一用面性
     * - `FacebookOutlined` / `TwitterOutlined`:品牌 logo 官方只有单一 Outlined 形态,保留
     */
    const Demo = () => (
      <div style={{ height: '100vh' }}>
        <ProLayout
          location={{
            pathname: '/favorites',
          }}
          route={{
            routes: [
              {
                path: '/favorites',
                name: '我的收藏',
                icon: <StarFilled />,
              },
              {
                path: '/social/facebook',
                name: 'Facebook',
                icon: <FacebookOutlined />,
              },
              {
                path: '/social/twitter',
                name: 'Twitter',
                icon: <TwitterOutlined />,
              },
              {
                path: '/svg-asset',
                name: 'SVG 资源',
                icon: 'https://gw.alipayobjects.com/zos/antfincdn/UCSef1BATc/nice.svg',
              },
            ],
          }}
        >
          <PageContainer content="欢迎使用">
            <div>Hello World</div>
          </PageContainer>
        </ProLayout>
      </div>
    );
    
    export default () => (
      <div>
        <Demo />
      </div>
    );
    
    Ghost Mode
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Descriptions } from 'antd';
    import defaultProps from './_defaultProps';
    
    const content = (
      <Descriptions size="small" column={2}>
        <Descriptions.Item label="创建人">书琰</Descriptions.Item>
        <Descriptions.Item label="联系方式">
          <a>421421</a>
        </Descriptions.Item>
        <Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
        <Descriptions.Item label="更新时间">2017-10-10</Descriptions.Item>
        <Descriptions.Item label="备注">
          中国浙江省杭州市西湖区古翠路
        </Descriptions.Item>
      </Descriptions>
    );
    
    const Demo = () => {
      return (
        <div
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            {...defaultProps}
            location={{
              pathname: '/welcome',
            }}
          >
            <PageContainer fixedHeader content={content}>
              <ProCard
                direction="column"
                ghost
                gutter={[0, 16]}
                style={{
                  height: '200vh',
                }}
              >
                <ProCard style={{ height: 200 }} />
                <ProCard gutter={16} ghost style={{ height: 200 }}>
                  <ProCard colSpan={16} />
                  <ProCard colSpan={8} />
                </ProCard>
              </ProCard>
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    

    Error boundaries

    Built-in error handling to avoid white screens
    import { PageContainer, ProLayout } from '@ant-design/pro-components';
    import { Button, Result } from 'antd';
    import type { ErrorInfo } from 'react';
    import React, { useState } from 'react';
    
    class CustomBoundary extends React.Component<
      Record<string, any>,
      { hasError: boolean; errorInfo: string }
    > {
      state = { hasError: false, errorInfo: '' };
    
      static getDerivedStateFromError(error: Error) {
        return { hasError: true, errorInfo: error.message };
      }
    
      componentDidCatch(error: any, errorInfo: ErrorInfo) {
        // You can also log the error to an error reporting service
        console.error(error, errorInfo);
      }
    
      render() {
        if (this.state.hasError) {
          // You can render any custom fallback UI
          return (
            <Result
              icon={
                <img
                  width={256}
                  src="https://gw.alipayobjects.com/zos/antfincdn/zIgkN%26mpMZ/shibaizhuangtaizuo.png"
                />
              }
              style={{
                height: '100%',
                background: '#fff',
              }}
              title="错误处理"
              extra={
                <>
                  <div
                    style={{
                      maxWidth: 620,
                      textAlign: 'start',
                      backgroundColor: 'rgba(255,229,100,0.3)',
                      borderInlineStartColor: '#ffe564',
                      borderInlineStartWidth: '9px',
                      borderInlineStartStyle: 'solid',
                      padding: '20px 45px 20px 26px',
                      margin: 'auto',
                      marginBlockEnd: '30px',
                      marginBlockStart: '20px',
                    }}
                  >
                    <p>注意</p>
                    <p>
                      错误边界<strong>无法</strong>捕获以下场景中产生的错误:
                    </p>
                    <ul
                      style={{
                        listStyle: 'none',
                      }}
                    >
                      <li>
                        事件处理(
                        <a href="https://zh-hans.reactjs.org/docs/error-boundaries.html#how-about-event-handlers#how-about-event-handlers">
                          了解更多
                        </a>
                        )
                      </li>
                      <li>
                        异步代码(例如 <code>setTimeout</code> 或{' '}
                        <code>requestAnimationFrame</code> 回调函数)
                      </li>
                      <li>服务端渲染</li>
                      <li>它自身抛出来的错误(并非它的子组件)</li>
                    </ul>
                  </div>
                  <Button
                    danger
                    type="primary"
                    onClick={() => {
                      window.location.reload();
                    }}
                  >
                    刷新页面
                  </Button>
                </>
              }
            />
          );
        }
        return this.props.children;
      }
    }
    
    const ErrorTriggerTestPage = () => {
      // default to throw error for snapshot test
      const [error, setError] = useState<boolean>(true);
      if (error) throw new Error('渲染发生了错误');
      return (
        <Button
          danger
          type="primary"
          onClick={() => {
            setError(true);
          }}
        >
          触发错误
        </Button>
      );
    };
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/default');
      return (
        <div
          id="test-pro-layout"
          style={{
            height: '100vh',
          }}
        >
          <ProLayout
            route={{
              path: '/',
              routes: [
                {
                  path: '/default',
                  name: '默认错误',
                },
                {
                  path: '/custom',
                  name: '自定义错误',
                },
              ],
            }}
            location={{
              pathname,
            }}
            menuItemRender={(item, dom) => (
              <a
                onClick={() => {
                  setPathname(item.path || '/welcome');
                }}
              >
                {dom}
              </a>
            )}
            ErrorBoundary={pathname === '/custom' ? CustomBoundary : undefined}
          >
            <PageContainer>
              <ErrorTriggerTestPage />
            </PageContainer>
          </ProLayout>
        </div>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    Dark compact theme
    import {
      GithubFilled,
      InfoCircleFilled,
      PlusCircleFilled,
      QuestionCircleFilled,
      SearchOutlined,
    } from '@ant-design/icons';
    import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
    import { Button, Input } from 'antd';
    import { useState } from 'react';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
    
      return (
        <ProLayout
          splitMenus
          layout="mix"
          token={{
            bgLayout: 'rgba(160, 217, 17, 0.1)',
            colorTextAppListIcon: 'rgba(255, 255, 255, 1)',
            colorTextAppListIconHover: 'rgba(245, 34, 45, 1)',
            sider: {
              colorBgMenuItemHover: 'rgba(24, 144, 255, 1)',
              colorMenuBackground: 'rgba(24, 144, 255, 0.1)',
              colorMenuItemDivider: 'rgba(250, 219, 20, 1)',
              colorTextMenu: 'rgba(250, 219, 20, 1)',
              colorTextMenuSelected: 'rgba(250, 84, 28, 1)',
              colorTextMenuItemHover: 'rgba(255, 255, 255, 1)',
              colorBgMenuItemSelected: 'rgb(82, 196, 26)',
              colorBgCollapsedButton: 'rgba(250, 84, 28, 1)',
              colorTextCollapsedButton: 'rgba(24, 144, 255, 1)',
              colorTextCollapsedButtonHover: 'rgba(143, 35, 35, 1)',
            },
            header: {
              colorBgMenuItemSelected: 'rgba(82, 196, 26, 1)',
              colorTextMenuSelected: 'rgba(114, 46, 209, 1)',
              colorBgHeader: 'rgba(250, 173, 20, 1)',
              colorHeaderTitle: 'rgba(47, 84, 235, 1)',
              colorTextMenuActive: 'rgba(255, 255, 255, 1)',
              colorTextMenu: 'rgba(250, 84, 28, 1)',
              colorBgMenuItemHover: 'rgba(24, 144, 255, 1)',
            },
            pageContainer: {
              colorBgPageContainer: 'rgba(250, 84, 28, 1)',
            },
          }}
          {...defaultProps}
          location={{
            pathname,
          }}
          avatarProps={{
            src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
            size: 'small',
            title: '书琰',
          }}
          actionsRender={(props) => {
            if (props.isMobile) return [];
            return [
              props.layout !== 'side' ? (
                <div
                  key="SearchOutlined"
                  aria-hidden
                  style={{
                    display: 'flex',
                    alignItems: 'center',
                    marginInlineEnd: 24,
                  }}
                  onMouseDown={(e) => {
                    e.stopPropagation();
                    e.preventDefault();
                  }}
                >
                  <Input
                    style={{
                      borderRadius: 4,
                      marginInlineEnd: 12,
                      backgroundColor: 'rgba(0,0,0,0.03)',
                    }}
                    prefix={
                      <SearchOutlined
                        style={{
                          color: 'rgba(0, 0, 0, 0.15)',
                        }}
                      />
                    }
                    placeholder="搜索方案"
                    variant="borderless"
                  />
                  <PlusCircleFilled
                    style={{
                      color: 'var(--ant-primary-color)',
                      fontSize: 24,
                    }}
                  />
                </div>
              ) : undefined,
              <InfoCircleFilled key="InfoCircleFilled" />,
              <QuestionCircleFilled key="QuestionCircleFilled" />,
              <GithubFilled key="GithubFilled" />,
            ];
          }}
          menuFooterRender={(props) => {
            if (props?.collapsed) return undefined;
            return (
              <p
                style={{
                  textAlign: 'center',
                  paddingBlockStart: 12,
                }}
              >
                Power by Ant Design
              </p>
            );
          }}
          onMenuHeaderClick={demoOnMenuHeaderClick}
          menuItemRender={(item, dom) => (
            <a
              onClick={() => {
                setPathname(item.path || '/welcome');
              }}
            >
              {dom}
            </a>
          )}
        >
          <PageContainer
            extra={[
              <Button key="3">操作</Button>,
              <Button key="2">操作</Button>,
              <Button key="1" type="primary">
                主操作
              </Button>,
            ]}
            footer={[
              <Button key="3">重置</Button>,
              <Button key="2" type="primary">
                提交
              </Button>,
            ]}
          >
            <ProCard
              style={{
                height: '200vh',
                minHeight: 800,
              }}
            >
              <div />
            </ProCard>
          </PageContainer>
        </ProLayout>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );
    
    splitMenus
    import {
      PageContainer,
      ProLayout,
      SettingDrawer,
    } from '@ant-design/pro-components';
    import defaultProps from './_defaultProps';
    import { demoOnMenuHeaderClick } from './_demoHandlers';
    
    const Demo = () => {
      return (
        <>
          <ProLayout {...defaultProps} layout="mix" splitMenus pure>
            <PageContainer>splitMenus + pure</PageContainer>
          </ProLayout>
          <ProLayout {...defaultProps} layout="mix" splitMenus>
            <PageContainer>splitMenus</PageContainer>
          </ProLayout>
          <ProLayout
            {...defaultProps}
            breadcrumbRender={false}
            layout="mix"
            splitMenus
            headerRender={false}
            onMenuHeaderClick={demoOnMenuHeaderClick}
            formatMessage={({ id }) => id}
            style={{
              height: '100vh',
            }}
          />
          <ProLayout
            {...defaultProps}
            layout="mix"
            menuExtraRender={() => 'dom'}
            menuHeaderRender={() => <div />}
            splitMenus
            location={{
              pathname: '/welcome',
            }}
            style={{
              height: '100vh',
            }}
          />
    
          <ProLayout
            {...defaultProps}
            layout="top"
            collapsed
            contentWidth="Fixed"
            menuExtraRender={false}
            menuHeaderRender={false}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
            }}
            location={{
              pathname: '/welcome',
            }}
            style={{
              height: '100vh',
            }}
          />
          <ProLayout
            {...defaultProps}
            layout="mix"
            menuHeaderRender={() => null}
            splitMenus
            fixSiderbar
            location={{
              pathname: '/welcome',
            }}
            contentWidth="Fixed"
            openKeys={false}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
            }}
            style={{
              height: '100vh',
            }}
          >
            <PageContainer>splitMenus + fixSiderbar</PageContainer>
          </ProLayout>
    
          <ProLayout
            {...defaultProps}
            layout="mix"
            menuHeaderRender={() => null}
            location={{
              pathname: '/welcome',
            }}
            openKeys={false}
            actionsRender={() => [
              <a key="docs" href="https://procomponents.ant.design/">
                文档
              </a>,
            ]}
            avatarProps={{
              src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
              size: 'small',
            }}
            style={{
              height: '100vh',
            }}
          >
            <PageContainer>顶部操作区示例</PageContainer>
          </ProLayout>
          <ProLayout
            {...defaultProps}
            layout="mix"
            menuHeaderRender={() => null}
            splitMenus={false}
            fixSiderbar
            location={{
              pathname: '/welcome',
            }}
            contentWidth="Fixed"
            openKeys={false}
            style={{
              height: '100vh',
            }}
          >
            <PageContainer>splitMenus 关闭时的侧栏与内容</PageContainer>
          </ProLayout>
          <SettingDrawer collapse />
        </>
      );
    };
    
    export default () => (
      <div style={{ padding: 24 }}>
        <Demo />
      </div>
    );