Back to Refine

Sider

documentation/versioned_docs/version-3.xx.xx/api-reference/antd/customization/sider.md

3.25.09.3 KB
Original Source

There are 2 ways that will allow you to customize your <Sider /> component if you need it.

You can access the logout, dashboard, items elements and collapsed state that we use in our default Sider component by using render properties. Customize it to your needs or you can create a custom <Sider /> component and use it either by passing it to <Refine /> or using a Custom Layout.

:::info-tip Swizzle You can swizzle this component to customize it with the refine CLI :::

Customize Sider by Using render property

tsx
setInitialRoutes(["/posts"]);

const PostList: React.FC = () => {
  return <div>Post List</div>;
};

// visible-block-start
import { Refine } from "@pankod/refine-core";
import { AntdLayout, Menu, Sider } from "@pankod/refine-antd";
import routerProvider from "@pankod/refine-react-router-v6";
import dataProvider from "@pankod/refine-simple-rest";

import { PostList } from "./pages/posts";

const App: React.FC = () => {
  const API_URL = "https://api.fake-rest.refine.dev";

  return (
    <Refine
      routerProvider={routerProvider}
      dataProvider={dataProvider(API_URL)}
      resources={[
        {
          name: "posts",
          list: PostList,
        },
      ]}
      Sider={Sider}
      Layout={({ children, Footer, Header, Sider, OffLayoutArea }) => {
        return (
          <AntdLayout style={{ minHeight: "100vh", flexDirection: "row" }}>
            <Sider
              render={({ items }) => {
                return (
                  <>
                    <Menu.Item
                      style={{
                        fontWeight: 700,
                      }}
                    >
                      Custom Element
                    </Menu.Item>
                    {items}
                  </>
                );
              }}
            />
            {Header && <Header />}
            <AntdLayout.Content>
              <div
                style={{
                  padding: 12,
                  minHeight: 360,
                }}
              >
                {children}
              </div>
              {OffLayoutArea && <OffLayoutArea />}
            </AntdLayout.Content>
            {Footer && <Footer />}
          </AntdLayout>
        );
      }}
    />
  );
};

// visible-block-end
render(<App />);

:::tip The Menu.Item component gives you an implemention ready component compatible with Sider menu items. If you want to add anything else to your Sider component, you can use the collapsed state to manage your component. :::

Recreating the Default Sider Menu

You can also customize your Sider component by creating the CustomSider component.

When you examine the code of the live-preview example below, you will see the same code that we used for the default sider component. You can create a customized CustomSider component for yourself by following this code.

:::info-tip Swizzle You can also run the swizzle command to export the source code of the default sider component. Refer to refine CLI for more information. :::

tsx
setInitialRoutes(["/posts"]);
const PostList: React.FC = () => {
  return <div>Post List</div>;
};

// visible-block-start
import {
  Refine,
  useTranslate,
  useLogout,
  useTitle,
  CanAccess,
  ITreeMenu,
  useIsExistAuthentication,
  useRouterContext,
  useMenu,
  useRefineContext,
} from "@pankod/refine-core";
import {
  Layout,
  AntdLayout,
  Grid,
  Icons,
  Menu,
  Title as DefaultTitle,
} from "@pankod/refine-antd";
import routerProvider from "@pankod/refine-react-router-v6";
import dataProvider from "@pankod/refine-simple-rest";

import { PostList } from "./pages/posts";

const API_URL = "https://api.fake-rest.refine.dev";

export type SiderRenderProps = {
  items: JSX.Element[];
  logout: React.ReactNode;
  dashboard: React.ReactNode;
};

export type RefineLayoutSiderProps = {
  render?: (props: SiderRenderProps) => React.ReactNode;
};

const { DashboardOutlined, LogoutOutlined, UnorderedListOutlined } = Icons;

const CustomSider: React.FC<RefineLayoutSiderProps> = ({ render }) => {
  const [collapsed, setCollapsed] = useState<boolean>(false);
  const isExistAuthentication = useIsExistAuthentication();
  const { Link } = useRouterContext();
  const { mutate: mutateLogout } = useLogout();
  const Title = useTitle();
  const translate = useTranslate();
  const { menuItems, selectedKey, defaultOpenKeys } = useMenu();
  const breakpoint = Grid.useBreakpoint();
  const { hasDashboard } = useRefineContext();

  const isMobile =
    typeof breakpoint.lg === "undefined" ? false : !breakpoint.lg;

  const RenderToTitle = Title ?? DefaultTitle;

  const renderTreeView = (tree: ITreeMenu[], selectedKey: string) => {
    return tree.map((item: ITreeMenu) => {
      const { icon, label, route, name, children, parentName } = item;

      if (children.length > 0) {
        return (
          <CanAccess
            key={route}
            resource={name.toLowerCase()}
            action="list"
            params={{
              resource: item,
            }}
          >
            <SubMenu
              key={route}
              icon={icon ?? <UnorderedListOutlined />}
              title={label}
            >
              {renderTreeView(children, selectedKey)}
            </SubMenu>
          </CanAccess>
        );
      }
      const isSelected = route === selectedKey;
      const isRoute = !(parentName !== undefined && children.length === 0);
      return (
        <CanAccess
          key={route}
          resource={name.toLowerCase()}
          action="list"
          params={{
            resource: item,
          }}
        >
          <Menu.Item
            key={route}
            style={{
              fontWeight: isSelected ? "bold" : "normal",
            }}
            icon={icon ?? (isRoute && <UnorderedListOutlined />)}
          >
            <Link to={route}>{label}</Link>
            {!collapsed && isSelected && (
              <div className="ant-menu-tree-arrow" />
            )}
          </Menu.Item>
        </CanAccess>
      );
    });
  };

  const logout = isExistAuthentication ? (
    <Menu.Item
      key="logout"
      onClick={() => mutateLogout()}
      icon={<LogoutOutlined />}
    >
      {translate("buttons.logout", "Logout")}
    </Menu.Item>
  ) : null;

  const dashboard = hasDashboard ? (
    <Menu.Item
      key="dashboard"
      style={{
        fontWeight: selectedKey === "/" ? "bold" : "normal",
      }}
      icon={<DashboardOutlined />}
    >
      <Link to="/">{translate("dashboard.title", "Dashboard")}</Link>
      {!collapsed && selectedKey === "/" && (
        <div className="ant-menu-tree-arrow" />
      )}
    </Menu.Item>
  ) : null;

  const items = renderTreeView(menuItems, selectedKey);

  const renderSider = () => {
    if (render) {
      return render({
        dashboard,
        items,
        logout,
      });
    }
    return (
      <>
        {dashboard}
        {items}
        {logout}
      </>
    );
  };

  const antLayoutSider: CSSProperties = {
    position: "relative",
  };
  const antLayoutSiderMobile: CSSProperties = {
    position: "fixed",
    height: "100vh",
    zIndex: 999,
  };

  return (
    <AntdLayout.Sider
      collapsible
      collapsed={collapsed}
      onCollapse={(collapsed: boolean): void => setCollapsed(collapsed)}
      collapsedWidth={isMobile ? 0 : 80}
      breakpoint="lg"
      style={isMobile ? antLayoutSiderMobile : antLayoutSider}
    >
      <RenderToTitle collapsed={collapsed} />
      <Menu
        selectedKeys={[selectedKey]}
        defaultOpenKeys={defaultOpenKeys}
        mode="inline"
        onClick={() => {
          if (!breakpoint.lg) {
            setCollapsed(true);
          }
        }}
      >
        {renderSider()}
      </Menu>
    </AntdLayout.Sider>
  );
};

const App: React.FC = () => {
  return (
    <Refine
      routerProvider={routerProvider}
      dataProvider={dataProvider(API_URL)}
      Layout={Layout}
      // highlight-next-line
      Sider={CustomSider}
      resources={[
        {
          name: "posts",
          list: PostList,
        },
      ]}
    />
  );
};

// visible-block-end

render(<App />);

:::tip If you want to create a multi-level menu, you can take a look at this multi-level menu example and also here is the guide. :::

useLogout provides the logout functionality.

:::caution useLogout hook can only be used if the authProvider is provided. Refer to authProvider docs for more detailed information. &#8594 Refer to useLogout docs for more detailed information. &#8594 :::

:::tip You can further customize the Sider and its appearance. Refer to Ant Design docs for more detailed information about Sider. &#8594 :::