App Shells

8 blocks

App shells establish the primary structure of a product interface. Built from flexible components, they can adapt to different layouts and alignments for each scenario—the examples below show only a few possible combinations.

App Shell 01

Preview
npx nateui@latest add AppShellSideWorkSpaceInset
Dark
import { useState } from 'react'
import classNames from '@/utils/classNames'
import AppShellInsetSide from '@/components/layouts/AppShellInsetSide'
import SideNav from '@/components/layouts/SideNav'
import Logo from '@/components/layouts/Logo'
import VerticalMenuContent from '@/components/layouts/VerticalMenuContent'
import Header from '@/components/layouts/Header'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import ToggleButton from '@/components/composites/ToggleButton'
import {
    PiCaretUpDown,
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiPulse,
    PiQuestion,
    PiShieldCheck,
    PiShoppingCart,
    PiSidebar,
    PiSidebarSimple,
    PiSignOut,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/VerticalMenuContent'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { ReactNode } from 'react'

const primaryNavigation: NavigationTree[] = [
    {
        key: 'main',
        path: '',
        title: 'Main',
        type: 'title',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        type: 'title',
        subMenu: [
            { key: 'user-management', path: '#user-management', title: 'User Management', icon: 'user-management', type: 'item' },
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
]

const secondaryNavigation: NavigationTree[] = [
    { key: 'settings', path: '#settings', title: 'Settings', icon: 'settings', type: 'item' },
    { key: 'help', path: '#help', title: 'Help', icon: 'help', type: 'item' },
]

const mobileNavigation: NavigationTree[] = [...primaryNavigation, ...secondaryNavigation]

const navigationIconMap: Record<string, ReactNode> = {
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    orders: <PiShoppingCart />,
    customers: <PiUsers />,
    'user-management': <PiUserGear />,
    'roles-permissions': <PiShieldCheck />,
    'activity-logs': <PiPulse />,
    settings: <PiGear />,
    help: <PiQuestion />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const AppShellSideWorkSpaceInset = () => {
    const [collapsed, setCollapsed] = useState(false)
    const [animating, setAnimating] = useState(false)

    const revealToggleOnHover = collapsed && !animating

    const handleToggleCollapse = () => {
        setCollapsed((prev) => !prev)
        setAnimating(true)
        setTimeout(() => setAnimating(false), 300)
    }

    return (
        <AppShellInsetSide
            sidebar={
                <SideNav
                    className={classNames('group/sidenav', collapsed && 'ltr:-mr-2 rtl:-ml-2')}
                    collapsed={collapsed}
                    menuVariant="subtle"
                    headerContent={
                        <div className={classNames('flex flex-col gap-2 pt-2', collapsed ? 'px-2' : 'px-4')}>
                            <div
                                className={classNames(
                                    'relative h-9 flex items-center px-0',
                                    collapsed ? 'w-15 -ms-2 justify-center' : 'justify-between',
                                )}
                            >
                                <div
                                    className={classNames(
                                        'transition-opacity duration-200 ease-in-out',
                                        revealToggleOnHover &&
                                            'group-hover/sidenav:opacity-0 group-hover/sidenav:pointer-events-none',
                                    )}
                                >
                                    <Logo
                                        src={`${assetBase}/logos/logo-collapsed.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: classNames('h-7', collapsed ? 'block dark:hidden' : 'hidden') }}
                                    />
                                    <Logo
                                        src={`${assetBase}/logos/logo-white-collapsed.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: classNames('h-7', collapsed ? 'hidden dark:block' : 'hidden') }}
                                    />
                                    <Logo
                                        src={`${assetBase}/logos/logo.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: classNames('h-7', collapsed ? 'hidden' : 'block dark:hidden') }}
                                    />
                                    <Logo
                                        src={`${assetBase}/logos/logo-white.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: classNames('h-7', collapsed ? 'hidden' : 'hidden dark:block') }}
                                    />
                                </div>
                                <div
                                    className={classNames(
                                        'flex items-center justify-center',
                                        !animating && 'transition-opacity duration-200 ease-in-out',
                                        collapsed && 'absolute inset-0 opacity-0',
                                        revealToggleOnHover && 'group-hover/sidenav:opacity-100',
                                    )}
                                >
                                    <ToggleButton
                                        active={collapsed}
                                        disabledActiveStyle
                                        inactiveContent={{ icon: <PiSidebarSimple /> }}
                                        activeContent={{ icon: <PiSidebar /> }}
                                        type="button"
                                        variant="ghost"
                                        aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
                                        onClick={handleToggleCollapse}
                                        className="flex items-center justify-center text-xl hover:bg-inverse/[.1]"
                                    />
                                </div>
                            </div>
                        </div>
                    }
                    footerContent={
                        <div className="flex flex-col justify-center border-t px-2 py-2">
                            <UserProfileDropdown
                                collapsed={collapsed}
                                menuClass="min-w-[240px]"
                                placement="right-end"
                                user={profileUser}
                                data={profileMenuItems}
                                customTrigger={({ avatar, userName, email }) => (
                                    <div className="flex w-full cursor-pointer items-center justify-between gap-2 rounded-lg p-2 transition-colors duration-150 hover:bg-accent">
                                        <div className="flex items-center gap-2">
                                            {avatar}
                                            {!collapsed && (
                                                <div>
                                                    {userName}
                                                    {email}
                                                </div>
                                            )}
                                        </div>
                                        {!collapsed && <PiCaretUpDown className="text-foreground" />}
                                    </div>
                                )}
                            />
                        </div>
                    }
                >
                    <VerticalMenuContent
                        collapsed={collapsed}
                        navigationTree={primaryNavigation}
                        activedRoute={{ key: 'dashboard' }}
                        menuVariant="subtle"
                        renderIcon={renderNavigationIcon}
                    />
                    <div className="flex-1" />
                    <VerticalMenuContent
                        collapsed={collapsed}
                        navigationTree={secondaryNavigation}
                        menuVariant="subtle"
                        renderIcon={renderNavigationIcon}
                    />
                </SideNav>
            }
            header={
                <Header
                    className="lg:hidden rounded-t-lg"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                    ]}
                    headerEnd={[
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                />
            }
        >
            <PageContainer footer={false}>
                {/* Your content goes here, replace with chlidren props... */}
                <div className="h-full min-h-64 overflow-hidden rounded-card border-2 border-dashed">
                    <svg className="h-full w-full" fill="none">
                        <defs>
                            <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                            </pattern>
                        </defs>
                        <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                    </svg>
                </div>
            </PageContainer>
        </AppShellInsetSide>
    )
}

export default AppShellSideWorkSpaceInset

App Shell 02

Preview
npx nateui@latest add AppShellSideSeamless
Dark
import { useState } from 'react'
import AppShellSeamlessSide from '@/components/layouts/AppShellSeamlessSide'
import SideNav from '@/components/layouts/SideNav'
import WorkspaceSelector from '@/components/layouts/WorkspaceSelector'
import VerticalMenuContent from '@/components/layouts/VerticalMenuContent'
import Header from '@/components/layouts/Header'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import NotificationToggle from '@/components/patterns/Notification/NotificationToggle'
import Search from '@/components/patterns/Search'
import ToggleButton from '@/components/composites/ToggleButton'
import {
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiPulse,
    PiQuestion,
    PiShieldCheck,
    PiShoppingCart,
    PiSidebar,
    PiSidebarSimple,
    PiSignOut,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/VerticalMenuContent'
import type { Workspace } from '@/components/layouts/WorkspaceSelector'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { ReactNode } from 'react'

const primaryNavigation: NavigationTree[] = [
    {
        key: 'main',
        path: '',
        title: 'Main',
        type: 'title',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        type: 'title',
        subMenu: [
            { key: 'user-management', path: '#user-management', title: 'User Management', icon: 'user-management', type: 'item' },
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
]

const secondaryNavigation: NavigationTree[] = [
    { key: 'settings', path: '#settings', title: 'Settings', icon: 'settings', type: 'item' },
    { key: 'help', path: '#help', title: 'Help', icon: 'help', type: 'item' },
]

const mobileNavigation: NavigationTree[] = [...primaryNavigation, ...secondaryNavigation]

const navigationIconMap: Record<string, ReactNode> = {
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    orders: <PiShoppingCart />,
    customers: <PiUsers />,
    'user-management': <PiUserGear />,
    'roles-permissions': <PiShieldCheck />,
    'activity-logs': <PiPulse />,
    settings: <PiGear />,
    help: <PiQuestion />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const workspaces: Workspace[] = [
    {
        id: 'tenant_001',
        name: 'NateUi',
        slug: 'nateui',
        logo: `${assetBase}/logos/logo-collapsed.svg`,
        description: '42 members',
        isDefault: true,
    },
    {
        id: 'tenant_002',
        name: 'Cloudora',
        slug: 'cloudora',
        logo: `${assetBase}/thumbs/projects/img-2.jpg`,
        description: '24 members',
        isDefault: false,
    },
    {
        id: 'tenant_003',
        name: 'Nexera',
        slug: 'nexera',
        logo: `${assetBase}/thumbs/projects/img-3.jpg`,
        description: '12 members',
        isDefault: false,
    },
    {
        id: 'tenant_004',
        name: 'Analytix',
        slug: 'analytix',
        logo: `${assetBase}/thumbs/projects/img-4.jpg`,
        description: '56 members',
        isDefault: false,
    },
]

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const NotificationTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <NotificationToggle dot aria-label="Notifications" {...props} />
)

const AppShellSideSeamless = () => {
    const [collapsed, setCollapsed] = useState(false)
    const [selectedWorkspace, setSelectedWorkspace] = useState(workspaces[0])

    return (
        <AppShellSeamlessSide
            sidebar={
                <SideNav
                    className="border-r bg-card"
                    collapsed={collapsed}
                    menuVariant="subtle"
                    headerContent={
                        <div className="p-2">
                            <WorkspaceSelector
                                collapsed={collapsed}
                                workspaces={workspaces}
                                selectedWorkspace={selectedWorkspace}
                                onWorkspaceSelect={setSelectedWorkspace}
                            />
                        </div>
                    }
                >
                    <VerticalMenuContent
                        collapsed={collapsed}
                        navigationTree={primaryNavigation}
                        activedRoute={{ key: 'dashboard' }}
                        menuVariant="subtle"
                        renderIcon={renderNavigationIcon}
                    />
                    <div className="flex-1" />
                    <VerticalMenuContent
                        collapsed={collapsed}
                        navigationTree={secondaryNavigation}
                        menuVariant="subtle"
                        renderIcon={renderNavigationIcon}
                    />
                </SideNav>
            }
            header={
                <Header
                    className="border-b"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                        {
                            component: (
                                <ToggleButton
                                    active={collapsed}
                                    disabledActiveStyle
                                    inactiveContent={{ icon: <PiSidebarSimple /> }}
                                    activeContent={{ icon: <PiSidebar /> }}
                                    type="button"
                                    variant="ghost"
                                    aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
                                    onClick={() => setCollapsed((prev) => !prev)}
                                    className="hidden items-center justify-center text-xl lg:flex"
                                />
                            ),
                        },
                    ]}
                    headerMiddle={
                        <div className="w-full max-w-xl">
                            <Search
                                trigger="input"
                                onQueryChange={() => {}}
                                onNavigate={() => {}}
                            />
                        </div>
                    }
                    headerEnd={[
                        {
                            component: NotificationTrigger,
                        },
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                />
            }
        >
            <PageContainer footer={false}>
                {/* Your content goes here, replace with chlidren props... */}
                <div className="h-full min-h-64 overflow-hidden rounded-card border-2 border-dashed">
                    <svg className="h-full w-full" fill="none">
                        <defs>
                            <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                            </pattern>
                        </defs>
                        <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                    </svg>
                </div>
            </PageContainer>
        </AppShellSeamlessSide>
    )
}

export default AppShellSideSeamless

App Shell 03

Preview
npx nateui@latest add AppShellDuoSide
Dark
import { useState, useEffect } from 'react'
import AppShellSeamlessSide from '@/components/layouts/AppShellSeamlessSide'
import StackedSideNav from '@/components/layouts/StackedSideNav'
import Logo from '@/components/layouts/Logo'
import Header from '@/components/layouts/Header'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import NotificationToggle from '@/components/patterns/Notification/NotificationToggle'
import Search from '@/components/patterns/Search'
import {
    PiBell,
    PiBriefcase,
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiLock,
    PiPackage,
    PiPulse,
    PiShield,
    PiShieldCheck,
    PiShoppingCart,
    PiSignOut,
    PiSlidersHorizontal,
    PiSquaresFour,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/VerticalMenuContent'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { ReactNode } from 'react'

const railNavigation: NavigationTree[] = [
    {
        key: 'overview',
        path: '',
        title: 'Overview',
        icon: 'overview',
        type: 'collapse',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
        ],
    },
    {
        key: 'management',
        path: '',
        title: 'Management',
        icon: 'management',
        type: 'collapse',
        subMenu: [
            { key: 'users', path: '#users', title: 'Users', icon: 'users', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'products', path: '#products', title: 'Products', icon: 'products', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        icon: 'administration',
        type: 'collapse',
        subMenu: [
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'staff-accounts', path: '#staff-accounts', title: 'Staff Accounts', icon: 'staff-accounts', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
    {
        key: 'settings',
        path: '',
        title: 'Settings',
        icon: 'settings',
        type: 'collapse',
        subMenu: [
            { key: 'general-settings', path: '#general-settings', title: 'General Settings', icon: 'general-settings', type: 'item' },
            { key: 'notifications', path: '#notifications', title: 'Notifications', icon: 'notifications', type: 'item' },
            { key: 'security', path: '#security', title: 'Security', icon: 'security', type: 'item' },
            { key: 'log-out', path: '#log-out', title: 'Log Out', icon: 'log-out', type: 'item' },
        ],
    },
]

const mobileNavigation: NavigationTree[] = railNavigation.map((group) => ({
    ...group,
    type: 'title',
}))

const includedRouteTree = {
    key: railNavigation[0].key,
    type: railNavigation[0].type,
    title: railNavigation[0].title,
    subMenu: railNavigation[0].subMenu,
}

const navigationIconMap: Record<string, ReactNode> = {
    overview: <PiSquaresFour />,
    management: <PiBriefcase />,
    administration: <PiShield />,
    settings: <PiGear />,
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    users: <PiUsers />,
    orders: <PiShoppingCart />,
    products: <PiPackage />,
    customers: <PiUser />,
    'roles-permissions': <PiShieldCheck />,
    'staff-accounts': <PiUserGear />,
    'activity-logs': <PiPulse />,
    'general-settings': <PiSlidersHorizontal />,
    notifications: <PiBell />,
    security: <PiLock />,
    'log-out': <PiSignOut />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const NotificationTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <NotificationToggle dot aria-label="Notifications" {...props} />
)

const useIsDarkMode = () => {
    const [isDark, setIsDark] = useState(false)

    useEffect(() => {
        const root = document.documentElement
        const update = () => setIsDark(root.classList.contains('dark'))
        update()
        const observer = new MutationObserver(update)
        observer.observe(root, { attributes: true, attributeFilter: ['class'] })
        return () => observer.disconnect()
    }, [])

    return isDark
}

const AppShellDuoSide = () => {
    const isDark = useIsDarkMode()

    return (
        <AppShellSeamlessSide
            sidebar={
                <StackedSideNav
                    mode={isDark ? 'dark' : 'light'}
                    direction="ltr"
                    navigationTree={railNavigation}
                    routeKey="dashboard"
                    activedRoute={{ key: 'dashboard' }}
                    includedRouteTree={includedRouteTree}
                    renderIcon={renderNavigationIcon}
                    logo={
                        <div className="flex h-16 items-center justify-center">
                            <Logo
                                src={isDark ? `${assetBase}/logos/logo-white-collapsed.svg` : `${assetBase}/logos/logo-collapsed.svg`}
                                alt="NateUI"
                                imgProps={{ className: 'h-7' }}
                            />
                        </div>
                    }
                />
            }
            header={
                <Header
                    className="border-b"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                    ]}
                    headerMiddle={
                        <div className="w-full max-w-xl">
                            <Search
                                trigger="input"
                                onQueryChange={() => {}}
                                onNavigate={() => {}}
                            />
                        </div>
                    }
                    headerEnd={[
                        {
                            component: NotificationTrigger,
                        },
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                />
            }
        >
            <PageContainer footer={false}>
                {/* Your content goes here, replace with chlidren props... */}
                <div className="h-full min-h-64 overflow-hidden rounded-card border-2 border-dashed">
                    <svg className="h-full w-full" fill="none">
                        <defs>
                            <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                            </pattern>
                        </defs>
                        <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                    </svg>
                </div>
            </PageContainer>
        </AppShellSeamlessSide>
    )
}

export default AppShellDuoSide

App Shell 04

Preview
npx nateui@latest add AppShellSideOffset
Dark
import { useState } from 'react'
import classNames from '@/utils/classNames'
import AppShellSeamlessSide from '@/components/layouts/AppShellSeamlessSide'
import SideNav from '@/components/layouts/SideNav'
import Logo from '@/components/layouts/Logo'
import VerticalMenuContent from '@/components/layouts/VerticalMenuContent'
import Header from '@/components/layouts/Header'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import ToggleButton from '@/components/composites/ToggleButton'
import {
    PiCaretUpDown,
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiPulse,
    PiQuestion,
    PiShieldCheck,
    PiShoppingCart,
    PiSidebar,
    PiSidebarSimple,
    PiSignOut,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/VerticalMenuContent'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { ReactNode } from 'react'

const primaryNavigation: NavigationTree[] = [
    {
        key: 'main',
        path: '',
        title: 'Main',
        type: 'title',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        type: 'title',
        subMenu: [
            { key: 'user-management', path: '#user-management', title: 'User Management', icon: 'user-management', type: 'item' },
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
]

const secondaryNavigation: NavigationTree[] = [
    { key: 'settings', path: '#settings', title: 'Settings', icon: 'settings', type: 'item' },
    { key: 'help', path: '#help', title: 'Help', icon: 'help', type: 'item' },
]

const mobileNavigation: NavigationTree[] = [...primaryNavigation, ...secondaryNavigation]

const navigationIconMap: Record<string, ReactNode> = {
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    orders: <PiShoppingCart />,
    customers: <PiUsers />,
    'user-management': <PiUserGear />,
    'roles-permissions': <PiShieldCheck />,
    'activity-logs': <PiPulse />,
    settings: <PiGear />,
    help: <PiQuestion />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const AppShellSideOffset = () => {
    const [collapsed, setCollapsed] = useState(false)
    const [animating, setAnimating] = useState(false)

    const revealToggleOnHover = collapsed && !animating

    const handleToggleCollapse = () => {
        setCollapsed((prev) => !prev)
        setAnimating(true)
        setTimeout(() => setAnimating(false), 300)
    }

    return (
        <AppShellSeamlessSide
            sidebar={
                <div className="hidden h-full bg-card p-2 lg:block">
                    <SideNav
                        className="group/sidenav h-[calc(100vh-1rem)] rounded-card bg-card border"
                        collapsed={collapsed}
                        menuVariant="subtle"
                        headerContent={
                            <div className={classNames('flex flex-col gap-2 pt-2', collapsed ? 'px-2' : 'px-4')}>
                                <div
                                    className={classNames(
                                        'relative h-9 flex items-center px-0',
                                        collapsed ? 'w-15 -ms-2 justify-center' : 'justify-between',
                                    )}
                                >
                                    <div
                                        className={classNames(
                                            'transition-opacity duration-200 ease-in-out',
                                            revealToggleOnHover &&
                                                'group-hover/sidenav:opacity-0 group-hover/sidenav:pointer-events-none',
                                        )}
                                    >
                                        <Logo
                                            src={`${assetBase}/logos/logo-collapsed.svg`}
                                            alt="NateUI"
                                            imgProps={{ className: classNames('h-7', collapsed ? 'block dark:hidden' : 'hidden') }}
                                        />
                                        <Logo
                                            src={`${assetBase}/logos/logo-white-collapsed.svg`}
                                            alt="NateUI"
                                            imgProps={{ className: classNames('h-7', collapsed ? 'hidden dark:block' : 'hidden') }}
                                        />
                                        <Logo
                                            src={`${assetBase}/logos/logo.svg`}
                                            alt="NateUI"
                                            imgProps={{ className: classNames('h-7', collapsed ? 'hidden' : 'block dark:hidden') }}
                                        />
                                        <Logo
                                            src={`${assetBase}/logos/logo-white.svg`}
                                            alt="NateUI"
                                            imgProps={{ className: classNames('h-7', collapsed ? 'hidden' : 'hidden dark:block') }}
                                        />
                                    </div>
                                    <div
                                        className={classNames(
                                            'flex items-center justify-center',
                                            !animating && 'transition-opacity duration-200 ease-in-out',
                                            collapsed && 'absolute inset-0 opacity-0',
                                            revealToggleOnHover && 'group-hover/sidenav:opacity-100',
                                        )}
                                    >
                                        <ToggleButton
                                            active={collapsed}
                                            disabledActiveStyle
                                            inactiveContent={{ icon: <PiSidebarSimple /> }}
                                            activeContent={{ icon: <PiSidebar /> }}
                                            type="button"
                                            variant="ghost"
                                            aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
                                            onClick={handleToggleCollapse}
                                            className="flex items-center justify-center text-xl hover:bg-inverse/[.1]"
                                        />
                                    </div>
                                </div>
                            </div>
                        }
                        footerContent={
                            <div className="flex flex-col justify-center border-t px-2 py-2">
                                <UserProfileDropdown
                                    collapsed={collapsed}
                                    menuClass="min-w-[240px]"
                                    placement="right-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                    customTrigger={({ avatar, userName, email }) => (
                                        <div className="flex w-full cursor-pointer items-center justify-between gap-2 rounded-lg p-2 transition-colors duration-150 hover:bg-accent">
                                            <div className="flex items-center gap-2">
                                                {avatar}
                                                {!collapsed && (
                                                    <div>
                                                        {userName}
                                                        {email}
                                                    </div>
                                                )}
                                            </div>
                                            {!collapsed && <PiCaretUpDown className="text-foreground" />}
                                        </div>
                                    )}
                                />
                            </div>
                        }
                    >
                        <VerticalMenuContent
                            collapsed={collapsed}
                            navigationTree={primaryNavigation}
                            activedRoute={{ key: 'dashboard' }}
                            menuVariant="subtle"
                            renderIcon={renderNavigationIcon}
                        />
                        <div className="flex-1" />
                        <VerticalMenuContent
                            collapsed={collapsed}
                            navigationTree={secondaryNavigation}
                            menuVariant="subtle"
                            renderIcon={renderNavigationIcon}
                        />
                    </SideNav>
                </div>
            }
            header={
                <Header
                    className="lg:hidden rounded-t-lg"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                    ]}
                    headerEnd={[
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                />
            }
        >
            <PageContainer footer={false}>
                {/* Your content goes here, replace with chlidren props... */}
                <div className="h-full min-h-64 overflow-hidden rounded-card border-2 border-dashed">
                    <svg className="h-full w-full" fill="none">
                        <defs>
                            <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                            </pattern>
                        </defs>
                        <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                    </svg>
                </div>
            </PageContainer>
        </AppShellSeamlessSide>
    )
}

export default AppShellSideOffset

App Shell 05

Preview
npx nateui@latest add AppShellTopStacked
Dark
import AppShellStacked from '@/components/layouts/AppShellStacked'
import Header from '@/components/layouts/Header'
import HorizontalNav from '@/components/layouts/HorizontalNav'
import Logo from '@/components/layouts/Logo'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import NotificationToggle from '@/components/patterns/Notification/NotificationToggle'
import Search from '@/components/patterns/Search'
import {
    PiBell,
    PiBriefcase,
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiLock,
    PiPackage,
    PiPulse,
    PiShield,
    PiShieldCheck,
    PiShoppingCart,
    PiSignOut,
    PiSlidersHorizontal,
    PiSquaresFour,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/HorizontalMenuContent'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { ReactNode } from 'react'

const navigationTree: NavigationTree[] = [
    {
        key: 'overview',
        path: '',
        title: 'Overview',
        icon: 'overview',
        type: 'collapse',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
        ],
    },
    {
        key: 'management',
        path: '',
        title: 'Management',
        icon: 'management',
        type: 'collapse',
        subMenu: [
            { key: 'users', path: '#users', title: 'Users', icon: 'users', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'products', path: '#products', title: 'Products', icon: 'products', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        icon: 'administration',
        type: 'collapse',
        subMenu: [
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'staff-accounts', path: '#staff-accounts', title: 'Staff Accounts', icon: 'staff-accounts', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
    {
        key: 'settings',
        path: '',
        title: 'Settings',
        icon: 'settings',
        type: 'collapse',
        subMenu: [
            { key: 'general-settings', path: '#general-settings', title: 'General Settings', icon: 'general-settings', type: 'item' },
            { key: 'notifications', path: '#notifications', title: 'Notifications', icon: 'notifications', type: 'item' },
            { key: 'security', path: '#security', title: 'Security', icon: 'security', type: 'item' },
        ],
    },
]

const mobileNavigation: NavigationTree[] = navigationTree.map((group) => ({
    ...group,
    type: 'title',
}))

const navigationIconMap: Record<string, ReactNode> = {
    overview: <PiSquaresFour />,
    management: <PiBriefcase />,
    administration: <PiShield />,
    settings: <PiGear />,
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    users: <PiUsers />,
    orders: <PiShoppingCart />,
    products: <PiPackage />,
    customers: <PiUser />,
    'roles-permissions': <PiShieldCheck />,
    'staff-accounts': <PiUserGear />,
    'activity-logs': <PiPulse />,
    'general-settings': <PiSlidersHorizontal />,
    notifications: <PiBell />,
    security: <PiLock />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const SearchTrigger = ({ className }: { className?: string; hoverable?: boolean }) => (
    <Search
        trigger="icon"
        onQueryChange={() => {}}
        onNavigate={() => {}}
        classNames={{ root: className }}
    />
)

const NotificationTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <NotificationToggle dot aria-label="Notifications" {...props} />
)


const AppShellTopStacked = () => {
    return (
        <AppShellStacked
            header={
                <Header
                    contained
                    className="border-b"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                        {
                            component: (
                                <div className="flex items-center">
                                    <Logo
                                        src={`${assetBase}/logos/logo.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: 'h-7 dark:hidden' }}
                                    />
                                    <Logo
                                        src={`${assetBase}/logos/logo-white.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: 'hidden h-7 dark:block' }}
                                    />
                                </div>
                            ),
                        },
                    ]}
                    headerMiddle={
                        <div className="absolute left-1/2 top-0 hidden h-full -translate-x-1/2 items-center lg:flex">
                            <HorizontalNav
                                currentRouteKey="dashboard"
                                navigationTree={navigationTree}
                                activedRoute={{ key: 'dashboard' }}
                                renderIcon={renderNavigationIcon}
                            />
                        </div>
                    }
                    headerEnd={[
                        { component: SearchTrigger },
                        { component: NotificationTrigger },
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                />
            }
        >
            <PageContainer footer={false}>
                {/* Your content goes here, replace with chlidren props... */}
                <div className="h-full min-h-64 overflow-hidden rounded-card border-2 border-dashed container mx-auto">
                    <svg className="h-full w-full" fill="none">
                        <defs>
                            <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                            </pattern>
                        </defs>
                        <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                    </svg>
                </div>
            </PageContainer>
        </AppShellStacked>
    )
}

export default AppShellTopStacked

App Shell 06

Preview
npx nateui@latest add AppShellTopStackedDuo
Dark
import { useState } from 'react'
import AppShellStacked from '@/components/layouts/AppShellStacked'
import Header from '@/components/layouts/Header'
import HorizontalNav from '@/components/layouts/HorizontalNav'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import WorkspaceSelector from '@/components/layouts/WorkspaceSelector'
import NotificationToggle from '@/components/patterns/Notification/NotificationToggle'
import Search from '@/components/patterns/Search/Search'
import {
    PiBell,
    PiBriefcase,
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiLock,
    PiPackage,
    PiPulse,
    PiShield,
    PiShieldCheck,
    PiShoppingCart,
    PiSignOut,
    PiSlidersHorizontal,
    PiSquaresFour,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/HorizontalMenuContent'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { Workspace } from '@/components/layouts/WorkspaceSelector'
import type { ReactNode } from 'react'

const navigationTree: NavigationTree[] = [
    {
        key: 'overview',
        path: '',
        title: 'Overview',
        icon: 'overview',
        type: 'collapse',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
        ],
    },
    {
        key: 'management',
        path: '',
        title: 'Management',
        icon: 'management',
        type: 'collapse',
        subMenu: [
            { key: 'users', path: '#users', title: 'Users', icon: 'users', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'products', path: '#products', title: 'Products', icon: 'products', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        icon: 'administration',
        type: 'collapse',
        subMenu: [
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'staff-accounts', path: '#staff-accounts', title: 'Staff Accounts', icon: 'staff-accounts', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
    {
        key: 'settings',
        path: '',
        title: 'Settings',
        icon: 'settings',
        type: 'collapse',
        subMenu: [
            { key: 'general-settings', path: '#general-settings', title: 'General Settings', icon: 'general-settings', type: 'item' },
            { key: 'notifications', path: '#notifications', title: 'Notifications', icon: 'notifications', type: 'item' },
            { key: 'security', path: '#security', title: 'Security', icon: 'security', type: 'item' },
        ],
    },
]

const mobileNavigation: NavigationTree[] = navigationTree.map((group) => ({
    ...group,
    type: 'title',
}))

const navigationIconMap: Record<string, ReactNode> = {
    overview: <PiSquaresFour />,
    management: <PiBriefcase />,
    administration: <PiShield />,
    settings: <PiGear />,
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    users: <PiUsers />,
    orders: <PiShoppingCart />,
    products: <PiPackage />,
    customers: <PiUser />,
    'roles-permissions': <PiShieldCheck />,
    'staff-accounts': <PiUserGear />,
    'activity-logs': <PiPulse />,
    'general-settings': <PiSlidersHorizontal />,
    notifications: <PiBell />,
    security: <PiLock />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const workspaces: Workspace[] = [
    {
        id: 'tenant_001',
        name: 'NateUi',
        slug: 'nateui',
        logo: `${assetBase}/logos/logo-collapsed.svg`,
        description: '42 members',
        isDefault: true,
    },
    {
        id: 'tenant_002',
        name: 'Cloudora',
        slug: 'cloudora',
        logo: `${assetBase}/thumbs/projects/img-2.jpg`,
        description: '24 members',
        isDefault: false,
    },
    {
        id: 'tenant_003',
        name: 'Nexera',
        slug: 'nexera',
        logo: `${assetBase}/thumbs/projects/img-3.jpg`,
        description: '12 members',
        isDefault: false,
    },
    {
        id: 'tenant_004',
        name: 'Analytix',
        slug: 'analytix',
        logo: `${assetBase}/thumbs/projects/img-4.jpg`,
        description: '56 members',
        isDefault: false,
    },
]

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const NotificationTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <NotificationToggle dot aria-label="Notifications" {...props} />
)

const AppShellTopStackedDuo = () => {
    const [selectedWorkspace, setSelectedWorkspace] = useState(workspaces[0])

    return (
        <AppShellStacked
            header={
                <Header
                    contained
                    className="border-b"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                        {
                            component: (
                                <WorkspaceSelector
                                    workspaces={workspaces}
                                    selectedWorkspace={selectedWorkspace}
                                    onWorkspaceSelect={setSelectedWorkspace}
                                />
                            ),
                        },
                    ]}
                    headerEnd={[
                        { component: NotificationTrigger },
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                    extended={{
                        className: 'hidden border-t lg:block',
                        content: (
                            <div className="flex h-14 w-full items-center justify-between gap-4">
                                <HorizontalNav
                                    currentRouteKey="dashboard"
                                    navigationTree={navigationTree}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                                <div className="w-full max-w-64">
                                    <Search
                                        trigger="input"
                                        onQueryChange={() => {}}
                                        onNavigate={() => {}}
                                    />
                                </div>
                            </div>
                        ),
                    }}
                />
            }
        >
            <PageContainer footer={false}>
                {/* Your content goes here, replace with chlidren props... */}
                <div className="h-full min-h-64 overflow-hidden rounded-card border-2 border-dashed container mx-auto">
                    <svg className="h-full w-full" fill="none">
                        <defs>
                            <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                            </pattern>
                        </defs>
                        <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                    </svg>
                </div>
            </PageContainer>
        </AppShellStacked>
    )
}

export default AppShellTopStackedDuo

App Shell 07

Preview
npx nateui@latest add AppShellTopStackedOverlap
Dark
import AppShellStackedOverlap from '@/components/layouts/AppShellStackedOverlap'
import Header from '@/components/layouts/Header'
import HorizontalNav from '@/components/layouts/HorizontalNav'
import Logo from '@/components/layouts/Logo'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import NotificationToggle from '@/components/patterns/Notification/NotificationToggle'
import Button from '@/components/ui/Button'
import {
    PiBell,
    PiBriefcase,
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiLock,
    PiPackage,
    PiPulse,
    PiShield,
    PiShieldCheck,
    PiShoppingCart,
    PiSignOut,
    PiSlidersHorizontal,
    PiSquaresFour,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/HorizontalMenuContent'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { ReactNode } from 'react'

const navigationTree: NavigationTree[] = [
    {
        key: 'overview',
        path: '',
        title: 'Overview',
        icon: 'overview',
        type: 'collapse',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
        ],
    },
    {
        key: 'management',
        path: '',
        title: 'Management',
        icon: 'management',
        type: 'collapse',
        subMenu: [
            { key: 'users', path: '#users', title: 'Users', icon: 'users', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'products', path: '#products', title: 'Products', icon: 'products', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        icon: 'administration',
        type: 'collapse',
        subMenu: [
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'staff-accounts', path: '#staff-accounts', title: 'Staff Accounts', icon: 'staff-accounts', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
    {
        key: 'settings',
        path: '',
        title: 'Settings',
        icon: 'settings',
        type: 'collapse',
        subMenu: [
            { key: 'general-settings', path: '#general-settings', title: 'General Settings', icon: 'general-settings', type: 'item' },
            { key: 'notifications', path: '#notifications', title: 'Notifications', icon: 'notifications', type: 'item' },
            { key: 'security', path: '#security', title: 'Security', icon: 'security', type: 'item' },
        ],
    },
]

const mobileNavigation: NavigationTree[] = navigationTree.map((group) => ({
    ...group,
    type: 'title',
}))

const navigationIconMap: Record<string, ReactNode> = {
    overview: <PiSquaresFour />,
    management: <PiBriefcase />,
    administration: <PiShield />,
    settings: <PiGear />,
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    users: <PiUsers />,
    orders: <PiShoppingCart />,
    products: <PiPackage />,
    customers: <PiUser />,
    'roles-permissions': <PiShieldCheck />,
    'staff-accounts': <PiUserGear />,
    'activity-logs': <PiPulse />,
    'general-settings': <PiSlidersHorizontal />,
    notifications: <PiBell />,
    security: <PiLock />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const NotificationTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <NotificationToggle dot aria-label="Notifications" {...props} />
)

const SettingsTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <Button variant="ghost" shape="round" icon={<PiGear />} aria-label="Settings" {...props} />
)

const AppShellTopStackedOverlap = () => {
    return (
        <AppShellStackedOverlap
            backgroundClass="dark bg-[linear-gradient(45deg,#4159d0_0%,#c84fc0_50%,#ffcd70_100%)]"
            header={
                <Header
                    contained
                    className="bg-transparent border-none [&_.badge-dot]:border-white"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                        {
                            component: (
                                <Logo
                                    src={`${assetBase}/logos/logo-white.svg`}
                                    alt="NateUI"
                                    imgProps={{ className: 'h-7' }}
                                />
                            ),
                        },
                        {
                            component: (
                                <HorizontalNav
                                    className="hidden lg:flex"
                                    currentRouteKey="dashboard"
                                    navigationTree={navigationTree}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                    ]}
                    headerEnd={[
                        { component: NotificationTrigger },
                        { component: SettingsTrigger },
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                />
            }
        >
            <PageContainer footer={false}>
                {/* Your content goes here, replace with chlidren props... */}
                <div className="h-full min-h-64 overflow-hidden rounded-card border-2 border-dashed bg-card shadow-card container mx-auto">
                    <svg className="h-full w-full" fill="none">
                        <defs>
                            <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                            </pattern>
                        </defs>
                        <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                    </svg>
                </div>
            </PageContainer>
        </AppShellStackedOverlap>
    )
}

export default AppShellTopStackedOverlap

App Shell 08

Preview
npx nateui@latest add AppShellTopStackedSplit
Dark
import AppShellStacked from '@/components/layouts/AppShellStacked'
import Header from '@/components/layouts/Header'
import Logo from '@/components/layouts/Logo'
import MobileNav from '@/components/layouts/MobileNav'
import PageContainer from '@/components/layouts/PageContainer'
import UserProfileDropdown from '@/components/layouts/UserProfileDropdown'
import VerticalMenuContent from '@/components/layouts/VerticalMenuContent'
import NotificationToggle from '@/components/patterns/Notification/NotificationToggle'
import Search from '@/components/patterns/Search/Search'
import Button from '@/components/ui/Button'
import {
    PiChartLine,
    PiClipboardText,
    PiGauge,
    PiGear,
    PiPulse,
    PiShieldCheck,
    PiShoppingCart,
    PiSignOut,
    PiUser,
    PiUserGear,
    PiUsers,
} from 'react-icons/pi'
import type { NavigationTree } from '@/components/layouts/VerticalMenuContent'
import type { DropdownItem } from '@/components/layouts/UserProfileDropdown'
import type { ReactNode } from 'react'

const primaryNavigation: NavigationTree[] = [
    {
        key: 'main',
        path: '',
        title: 'Main',
        type: 'title',
        subMenu: [
            { key: 'dashboard', path: '#dashboard', title: 'Dashboard', icon: 'dashboard', type: 'item' },
            { key: 'analytics', path: '#analytics', title: 'Analytics', icon: 'analytics', type: 'item' },
            { key: 'reports', path: '#reports', title: 'Reports', icon: 'reports', type: 'item' },
            { key: 'orders', path: '#orders', title: 'Orders', icon: 'orders', type: 'item' },
            { key: 'customers', path: '#customers', title: 'Customers', icon: 'customers', type: 'item' },
        ],
    },
    {
        key: 'administration',
        path: '',
        title: 'Administration',
        type: 'title',
        subMenu: [
            { key: 'user-management', path: '#user-management', title: 'User Management', icon: 'user-management', type: 'item' },
            { key: 'roles-permissions', path: '#roles-permissions', title: 'Roles & Permissions', icon: 'roles-permissions', type: 'item' },
            { key: 'activity-logs', path: '#activity-logs', title: 'Activity Logs', icon: 'activity-logs', type: 'item' },
        ],
    },
]

const mobileNavigation: NavigationTree[] = primaryNavigation

const navigationIconMap: Record<string, ReactNode> = {
    dashboard: <PiGauge />,
    analytics: <PiChartLine />,
    reports: <PiClipboardText />,
    orders: <PiShoppingCart />,
    customers: <PiUsers />,
    'user-management': <PiUserGear />,
    'roles-permissions': <PiShieldCheck />,
    'activity-logs': <PiPulse />,
}

const renderNavigationIcon = (icon: string) => navigationIconMap[icon] ?? null

const assetBase = 'https://statics.nateui.com/img'

const profileUser = {
    name: 'Angelina Gotelli',
    email: 'admin-01@nateui.com',
    image: `${assetBase}/avatars/thumb-1.jpg`,
}

const profileMenuItems: DropdownItem[] = [
    { type: 'link', label: 'Profile', path: '#profile', icon: <PiUser /> },
    { type: 'link', label: 'Account settings', path: '#account-settings', icon: <PiGear /> },
    { type: 'link', label: 'Activity log', path: '#activity-log', icon: <PiPulse /> },
    { type: 'divider' },
    { type: 'link', label: 'Sign out', path: '#sign-out', icon: <PiSignOut /> },
]

const NotificationTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <NotificationToggle dot aria-label="Notifications" {...props} />
)

const SettingsTrigger = (props: { className?: string; hoverable?: boolean }) => (
    <Button variant="ghost" shape="round" icon={<PiGear />} aria-label="Settings" {...props} />
)

const AppShellTopStackedSplit = () => {
    return (
        <AppShellStacked
            header={
                <Header
                    contained
                    className="border-b"
                    headerStart={[
                        {
                            component: (
                                <MobileNav
                                    navigationTree={mobileNavigation}
                                    activedRoute={{ key: 'dashboard' }}
                                    renderIcon={renderNavigationIcon}
                                />
                            ),
                        },
                        {
                            component: (
                                <div className="flex items-center">
                                    <Logo
                                        src={`${assetBase}/logos/logo.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: 'h-7 dark:hidden' }}
                                    />
                                    <Logo
                                        src={`${assetBase}/logos/logo-white.svg`}
                                        alt="NateUI"
                                        imgProps={{ className: 'hidden h-7 dark:block' }}
                                    />
                                </div>
                            ),
                        },
                    ]}
                    headerMiddle={
                        <div className="w-full max-w-xl">
                            <Search
                                trigger="input"
                                onQueryChange={() => {}}
                                onNavigate={() => {}}
                            />
                        </div>
                    }
                    headerEnd={[
                        { component: NotificationTrigger },
                        { component: SettingsTrigger },
                        {
                            component: (
                                <UserProfileDropdown
                                    collapsed
                                    placement="bottom-end"
                                    user={profileUser}
                                    data={profileMenuItems}
                                />
                            ),
                        },
                    ]}
                />
            }
        >
            <PageContainer footer={false}>
                <div className="flex h-full gap-4">
                    <div className="hidden w-56 shrink-0 lg:block">
                        <VerticalMenuContent
                            navigationTree={primaryNavigation}
                            activedRoute={{ key: 'dashboard' }}
                            menuVariant="subtle"
                            renderIcon={renderNavigationIcon}
                        />
                    </div>
                    {/* Your content goes here, replace with chlidren props... */}
                    <div className="h-full min-h-64 flex-1 overflow-hidden rounded-card border-2 border-dashed">
                        <svg className="h-full w-full" fill="none">
                            <defs>
                                <pattern id="diagonal-stripes" width="8" height="8" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
                                    <line x1="0" y1="0" x2="0" y2="8" stroke="var(--nui-muted)" strokeWidth="2" />
                                </pattern>
                            </defs>
                            <rect width="100%" height="100%" fill="url(#diagonal-stripes)" />
                        </svg>
                    </div>
                </div>
            </PageContainer>
        </AppShellStacked>
    )
}

export default AppShellTopStackedSplit