Basic · Layout

Layout

Used to quickly divide the overall layout of the page

Overview

  • Layout: Layout containers. You can nest Header Sider Content Footer or Layout itself inside.
  • Header: Head component, can only be used inside Layout.
  • Sider: Sidebar, can only be used inside Layout.
  • Content: Content component, can only be used inside Layout.
  • Footer: Footer component, can only be used inside Layout.

Demos

How to import

<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';
</script>

Use the subcomponents as Layout.Header, Layout.Sider, Layout.Content and Layout.Footer, or import LayoutHeader, LayoutSider, LayoutContent and LayoutFooter by name.

Three-section Layout

Header
Content
Footer
Code
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>
Header
Content
Footer
Code
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout>
            <Layout.Sider :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }">Sider</Layout.Sider>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
        </Layout>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>
Header
Content
Footer
Code
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
            <Layout.Sider :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }">Sider</Layout.Sider>
        </Layout>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>
Header
Content
Footer
Code
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Sider :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }">Sider</Layout.Sider>
        <Layout>
            <Layout.Header :style="commonStyle">Header</Layout.Header>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
            <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
        </Layout>
    </Layout>
</template>

Responsive Layout

Six response sizes are preset in the sidebar: xs,sm,md,lg,xl,xxl. You can use breakpoint to set breakpoints.

Listen to @breakpoint for changes in whether a breakpoint matches.

Header
Content
Footer
Code
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout>
            <Layout.Sider :breakpoint="['md']" :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }" @breakpoint="(screen, matches) => console.log(screen, matches)"> Sider </Layout.Sider>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
        </Layout>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>

Layout Examples

Top-nav Layout

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服反馈建议
Code
<script setup lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrous/semi-icons-vue';
    import { Avatar, Breadcrumb, Button, Layout, Nav, Skeleton } from '@fibrous/semi-ui-vue';

    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = { color: 'var(--semi-color-text-2)', marginRight: '12px' };
</script>

<template>
    <Layout :style="{ border: '1px solid var(--semi-color-border)' }">
        <Layout.Header :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
            <div>
                <Nav :defaultSelectedKeys="['Home']" mode="horizontal">
                    <Nav.Header>
                        <IconSemiLogo :style="{ fontSize: '36px' }" />
                    </Nav.Header>
                    <Nav.Item itemKey="Home" text="首页">
                        <template #icon>
                            <IconHome size="large" />
                        </template>
                    </Nav.Item>
                    <Nav.Item itemKey="Live" text="直播">
                        <template #icon>
                            <IconLive size="large" />
                        </template>
                    </Nav.Item>
                    <Nav.Item itemKey="Setting" text="设置">
                        <template #icon>
                            <IconSetting size="large" />
                        </template>
                    </Nav.Item>
                    <Nav.Footer>
                        <Button aria-label="通知" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconBell size="large" />
                            </template>
                        </Button>
                        <Button aria-label="帮助" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconHelpCircle size="large" />
                            </template>
                        </Button>
                        <Avatar color="orange" size="small">YJ</Avatar>
                    </Nav.Footer>
                </Nav>
            </div>
        </Layout.Header>
        <Layout.Content :style="{ padding: '24px', backgroundColor: 'var(--semi-color-bg-0)' }">
            <Breadcrumb :routes="routes" :style="{ marginBottom: '24px' }" />
            <div
                :style="{
                    borderRadius: '10px',
                    border: '1px solid var(--semi-color-border)',
                    height: '376px',
                    padding: '32px',
                }"
            >
                <Skeleton loading>
                    <template #placeholder>
                        <Skeleton.Paragraph :rows="2" />
                    </template>
                    <p>Hi, Bytedance dance dance.</p>
                    <p>Hi, Bytedance dance dance.</p>
                </Skeleton>
            </div>
        </Layout.Content>
        <Layout.Footer
            :style="{
                display: 'flex',
                justifyContent: 'space-between',
                padding: '20px',
                color: 'var(--semi-color-text-2)',
                backgroundColor: 'var(--semi-grey-0)',
            }"
        >
            <span :style="{ display: 'flex', alignItems: 'center' }">
                <IconBytedanceLogo size="large" :style="{ marginRight: '8px' }" />
                <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
            </span>
            <span>
                <span :style="{ marginRight: '24px' }">平台客服</span>
                <span>反馈建议</span>
            </span>
        </Layout.Footer>
    </Layout>
</template>

Top-Nav SideBar Layout

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服反馈建议
Code
<script setup lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHistogram, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrous/semi-icons-vue';
    import { Avatar, Breadcrumb, Button, Layout, Nav, Skeleton, type NavItemConfig, type NavItems } from '@fibrous/semi-ui-vue';

    const items: NavItems = [
        { itemKey: 'Home', text: '首页' },
        { itemKey: 'Histogram', text: '基础数据' },
        { itemKey: 'Live', text: '测试功能' },
        { itemKey: 'Setting', text: '设置' },
    ];
    const icons = { Home: IconHome, Histogram: IconHistogram, Live: IconLive, Setting: IconSetting };
    const iconOf = (item: NavItemConfig) => icons[item.itemKey as keyof typeof icons];
    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = { color: 'var(--semi-color-text-2)', marginRight: '12px' };
</script>

<template>
    <Layout :style="{ border: '1px solid var(--semi-color-border)' }">
        <Layout.Header :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
            <div>
                <Nav aria-label="顶部导航" :defaultSelectedKeys="['Home']" mode="horizontal">
                    <Nav.Header>
                        <IconSemiLogo :style="{ height: '36px', fontSize: '36px' }" />
                    </Nav.Header>
                    <span :style="{ color: 'var(--semi-color-text-2)' }">
                        <span :style="{ marginRight: '24px', color: 'var(--semi-color-text-0)', fontWeight: '600' }"> 模版推荐 </span>
                        <span :style="{ marginRight: '24px' }">所有模版</span>
                        <span>我的模版</span>
                    </span>
                    <Nav.Footer>
                        <Button aria-label="通知" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconBell size="large" />
                            </template>
                        </Button>
                        <Button aria-label="帮助" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconHelpCircle size="large" />
                            </template>
                        </Button>
                        <Avatar color="orange" size="small">YJ</Avatar>
                    </Nav.Footer>
                </Nav>
            </div>
        </Layout.Header>
        <Layout>
            <Layout.Sider :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
                <Nav aria-label="侧边导航" :defaultSelectedKeys="['Home']" :footer="{ collapseButton: true }" :items="items" :style="{ maxWidth: '220px', height: '100%' }">
                    <template #icon="{ item }">
                        <component :is="iconOf(item)" size="large" />
                    </template>
                </Nav>
            </Layout.Sider>
            <Layout.Content :style="{ padding: '24px', backgroundColor: 'var(--semi-color-bg-0)' }">
                <Breadcrumb :routes="routes" :style="{ marginBottom: '24px' }" />
                <div
                    :style="{
                        borderRadius: '10px',
                        border: '1px solid var(--semi-color-border)',
                        height: '376px',
                        padding: '32px',
                    }"
                >
                    <Skeleton loading>
                        <template #placeholder>
                            <Skeleton.Paragraph :rows="2" />
                        </template>
                        <p>Hi, Bytedance dance dance.</p>
                        <p>Hi, Bytedance dance dance.</p>
                    </Skeleton>
                </div>
            </Layout.Content>
        </Layout>
        <Layout.Footer
            :style="{
                display: 'flex',
                justifyContent: 'space-between',
                padding: '20px',
                color: 'var(--semi-color-text-2)',
                backgroundColor: 'var(--semi-grey-0)',
            }"
        >
            <span :style="{ display: 'flex', alignItems: 'center' }">
                <IconBytedanceLogo size="large" :style="{ marginRight: '8px' }" />
                <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
            </span>
            <span>
                <span :style="{ marginRight: '24px' }">平台客服</span>
                <span>反馈建议</span>
            </span>
        </Layout.Footer>
    </Layout>
</template>
Copyright © 2019 ByteDance. All Rights Reserved. 平台客服反馈建议
Code
<script setup lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHistogram, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrous/semi-icons-vue';
    import { Avatar, Breadcrumb, Button, Layout, Nav, Skeleton, type NavItemConfig, type NavItems } from '@fibrous/semi-ui-vue';

    const items: NavItems = [
        { itemKey: 'Home', text: '首页' },
        { itemKey: 'Histogram', text: '基础数据' },
        { itemKey: 'Live', text: '测试功能' },
        { itemKey: 'Setting', text: '设置' },
    ];
    const icons = { Home: IconHome, Histogram: IconHistogram, Live: IconLive, Setting: IconSetting };
    const iconOf = (item: NavItemConfig) => icons[item.itemKey as keyof typeof icons];
    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = { color: 'var(--semi-color-text-2)', marginRight: '12px' };
</script>

<template>
    <Layout :style="{ border: '1px solid var(--semi-color-border)' }">
        <Layout.Sider :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
            <Nav aria-label="侧边导航" :defaultSelectedKeys="['Home']" :footer="{ collapseButton: true }" :items="items" :style="{ maxWidth: '220px', height: '100%' }">
                <template #icon="{ item }">
                    <component :is="iconOf(item)" size="large" />
                </template>
                <Nav.Header text="Semi Design">
                    <template #logo>
                        <IconSemiLogo :style="{ fontSize: '36px' }" />
                    </template>
                </Nav.Header>
            </Nav>
        </Layout.Sider>
        <Layout>
            <Layout.Header :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
                <Nav aria-label="顶部导航" mode="horizontal">
                    <template #footer>
                        <Button aria-label="通知" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconBell size="large" />
                            </template>
                        </Button>
                        <Button aria-label="帮助" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconHelpCircle size="large" />
                            </template>
                        </Button>
                        <Avatar color="orange" size="small">YJ</Avatar>
                    </template>
                </Nav>
            </Layout.Header>
            <Layout.Content :style="{ padding: '24px', backgroundColor: 'var(--semi-color-bg-0)' }">
                <Breadcrumb :routes="routes" :style="{ marginBottom: '24px' }" />
                <div
                    :style="{
                        borderRadius: '10px',
                        border: '1px solid var(--semi-color-border)',
                        height: '376px',
                        padding: '32px',
                    }"
                >
                    <Skeleton loading>
                        <template #placeholder>
                            <Skeleton.Paragraph :rows="2" />
                        </template>
                        <p>Hi, Bytedance dance dance.</p>
                        <p>Hi, Bytedance dance dance.</p>
                    </Skeleton>
                </div>
            </Layout.Content>
            <Layout.Footer
                :style="{
                    display: 'flex',
                    justifyContent: 'space-between',
                    padding: '20px',
                    color: 'var(--semi-color-text-2)',
                    backgroundColor: 'var(--semi-grey-0)',
                }"
            >
                <span :style="{ display: 'flex', alignItems: 'center' }">
                    <IconBytedanceLogo size="large" :style="{ marginRight: '8px' }" />
                    <span>Copyright © 2019 ByteDance. All Rights Reserved. </span>
                </span>
                <span>
                    <span :style="{ marginRight: '24px' }">平台客服</span>
                    <span>反馈建议</span>
                </span>
            </Layout.Footer>
        </Layout>
    </Layout>
</template>

API Reference

Layout

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix.stringsemiDefault("Layout", "prefixCls", cssClasses.PREFIX)
tagNameTag name of the root element.keyof HTMLElementTagNameMapsemiDefault("Layout", "tagName", "section")
hasSiderIndicates that a Sider is inside. A Sider among the direct children is detected automatically; a deeper one registers after mount, so pass true during server rendering to avoid style flashing. Passing false cannot remove the has-sider class that a Sider adds.booleansemiGlobalDefault("Layout", "hasSider")

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
defaultContent of the Layout: Layout.Header, Layout.Sider, Layout.Content, Layout.Footer or a nested Layout.any

Exposes

NameDescriptionType
None

class, style and native attributes (including role and aria-*) fall through to the root element.

Layout.Header, Layout.Footer, Layout.Content

The three share the same API. Their default tags are header, footer and main.

Layout.Header

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix.stringcssClasses.PREFIX
tagNameTag name of the root element.keyof HTMLElementTagNameMap"header"
typeFor internal use; decides the class name suffix.string"header"

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
defaultContent of the header.any

Exposes

NameDescriptionType
None

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix.stringcssClasses.PREFIX
tagNameTag name of the root element.keyof HTMLElementTagNameMap"footer"
typeFor internal use; decides the class name suffix.string"footer"

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
defaultContent of the footer.any

Exposes

NameDescriptionType
None

Layout.Content

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix.stringcssClasses.PREFIX
tagNameTag name of the root element.keyof HTMLElementTagNameMap"main"
typeFor internal use; decides the class name suffix.string"content"

Events

EventDescriptionCallback parameters
None

Slots

SlotDescriptionScope parameters
defaultContent of the main area.any

Exposes

NameDescriptionType
None

class, style and native attributes (including role and aria-*) fall through to each root element.

Layout.Sider

Props

PropertyDescriptionTypeDefault
prefixClsClass name prefix.stringcssClasses.PREFIX
breakpointBreakpoints that trigger the responsive layout: xs, sm, md, lg, xl or xxl. Read once at mount.Breakpoint[]undefined

Events

EventDescriptionCallback parameters
breakpointEmitted once at mount for each screen listed in breakpoint with the current viewport state, then whenever it starts or stops matching, with the screen name and whether it matches.[screen: Breakpoint, match: boolean]

Slots

SlotDescriptionScope parameters
defaultContent of the sider.any

Exposes

NameDescriptionType
None

class, style and native attributes (including role and aria-*) fall through to the root aside element.

responsive map

{
  xs: '(max-width: 575px)',
  sm: '(min-width: 576px)',
  md: '(min-width: 768px)',
  lg: '(min-width: 992px)',
  xl: '(min-width: 1200px)',
  xxl: '(min-width: 1600px)',
}

Accessibility

ARIA

  • Sider renders an aside; pass aria-label to describe what this Sider is for.
  • Header and Footer render header and footer by default. Inside the section of a Layout they are not banner or contentinfo landmarks; pass role="banner" or role="contentinfo" when they are the header or footer of the whole page.
  • Content renders main by default, and a page should have only one main; when nesting, use tagName to switch to another tag.
  • Header, Content and Footer accept role and aria-label to describe the corresponding element.