Profile header + grouped sections
Drawer panel with a profile header (avatar, name, email, plan Badge), labelled sections (Main, Workspace, Support) and Log out pinned in the footer.
Light
Dark
Your app is ready.
Inter body text, Inter headings.
a0: Vega, neutral, neutral accent, default radius, Inter.Change PresetInstallation
npx nativecn-cli@latest add drawer-01Also installs: drawer, avatar, badge, separator, text, theme.
Packages: lucide-react-native@^1.51.0, react-native-svg.
What makes it different
The familiar Gmail/Slack drawer: who is signed in on top, items grouped under small headings, Log out at the bottom; the active item sits on a muted background.
Block Variants
drawer-01(this page) – Profile header + grouped sections: The familiar Gmail/Slack drawer: who is signed in on top, items grouped under small headings, Log out at the bottom; the active item sits on a muted background.drawer-02– SaaS workspace: A product workspace: an Accent-tinted onboarding Checklist on top, a second group pushed to the bottom, and a team switcher and account button in the footer instead of a profile header.drawer-03– Cover header: A consumer or commerce app drawer: a dark profile cover that stays dark in both Schemes (with a light status bar over it while the drawer is open), and items that show the current route by Accent Colour text and icon, with no background.
Accessibility
- Each item is a DrawerItem: role link with aria-selected while its route is active, or role button for an action without href.
- An item's visible label is its accessible name.
- Items are 48 high in Vega and 40 in Nova, with the tap area extended to 48.
- The section titles (Main, Workspace, Support) have role heading.
- The name in the header is a heading (h4); the initials Avatar is decorative.
- Log out is an action item: role button.
Notes
In app/(drawer)/_layout.tsx, with Expo Router's drawer from expo-router/drawer (never @react-navigation/drawer or the npm package expo-drawer): <Drawer drawerContent={(props) => <Drawer01 {...props} />} />. Spread every drawerContent prop in, so items can close the drawer. Tapping an item navigates and closes the drawer; the backdrop and the system back gesture also close it. Names, avatars, counts, items and routes are placeholders: rename them and point each href at a route in your drawer Layout. Item heights follow the drawer.item Style Slot (Vega 48, Nova 40), and the panel keeps clear of the status bar and home indicator.
Source
The Vega Style's code, exactly as add copies it into your app.
{components}/drawer-01/index.tsx
// drawer-01: profile header + grouped sections. The panel of a drawer Layout, built from the
// drawer Components on Expo Router's own drawer (`expo-router/drawer`; never install
// `@react-navigation/drawer` or the unrelated npm package `expo-drawer`).
//
// In app/(drawer)/_layout.tsx:
// <Drawer
// drawerContent={(props) => <Drawer01 {...props} onLogOut={signOut} />}
// screenOptions={{ drawerStyle: { width: 300 } }}
// />
//
// The name, email, plan, items and routes are placeholders: rename them to your app's.
import {
BarChart3,
FolderKanban,
House,
Inbox,
LifeBuoy,
LogOut,
Settings,
Users,
} from "lucide-react-native";
import { View } from "react-native";
import { Avatar } from "@/components/avatar";
import { Badge } from "@/components/badge";
import {
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerItem,
DrawerSection,
type DrawerContentProps,
} from "@/components/drawer";
import { Separator } from "@/components/separator";
import { Text } from "@/components/text";
import { createStyles } from "@/theme";
export type Drawer01Props = DrawerContentProps & {
/** Runs when Log out is pressed. The drawer stays open; sign the user out here. */
onLogOut?: () => void;
};
export function Drawer01({ onLogOut, ...props }: Drawer01Props) {
const styles = useStyles();
return (
<DrawerContent {...props}>
<DrawerHeader style={styles.header}>
<View style={styles.profile}>
<Avatar fallback="JD" />
<View style={styles.identity}>
<Text variant="h4" numberOfLines={1}>
Jane Doe
</Text>
<Text variant="small" color="mutedForeground" numberOfLines={1}>
jane@example.com
</Text>
</View>
<Badge label="Pro" />
</View>
</DrawerHeader>
<DrawerSection title="Main">
<DrawerItem label="Home" icon={House} href="/" />
<DrawerItem label="Inbox" icon={Inbox} href="/inbox" badge={12} />
<DrawerItem label="Analytics" icon={BarChart3} href="/analytics" />
</DrawerSection>
<DrawerSection title="Workspace">
<DrawerItem label="Projects" icon={FolderKanban} href="/projects" />
<DrawerItem label="Team" icon={Users} href="/team" />
</DrawerSection>
<DrawerSection title="Support">
<DrawerItem label="Settings" icon={Settings} href="/settings" />
<DrawerItem label="Help" icon={LifeBuoy} href="/help" />
</DrawerSection>
<DrawerFooter>
<Separator />
<DrawerItem label="Log out" icon={LogOut} onPress={onLogOut} />
</DrawerFooter>
</DrawerContent>
);
}
const useStyles = createStyles((t) => ({
header: {
borderBottomWidth: t.borderWidth.default,
borderBottomColor: t.colors.border,
},
profile: { flexDirection: "row", alignItems: "center", gap: t.spacing[3] },
identity: { flex: 1, minWidth: 0 },
}));{components}/drawer-01/index.tsx
// drawer-01: profile header + grouped sections. The panel of a drawer Layout, built from the
// drawer Components on Expo Router's own drawer (`expo-router/drawer`; never install
// `@react-navigation/drawer` or the unrelated npm package `expo-drawer`).
//
// In app/(drawer)/_layout.tsx:
// <Drawer
// drawerContent={(props) => <Drawer01 {...props} onLogOut={signOut} />}
// screenOptions={{ drawerStyle: { width: 300 } }}
// />
//
// The name, email, plan, items and routes are placeholders: rename them to your app's.
import {
BarChart3,
FolderKanban,
House,
Inbox,
LifeBuoy,
LogOut,
Settings,
Users,
} from "lucide-react-native";
import { View } from "react-native";
import { Avatar } from "@/components/avatar";
import { Badge } from "@/components/badge";
import {
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerItem,
DrawerSection,
type DrawerContentProps,
} from "@/components/drawer";
import { Separator } from "@/components/separator";
import { Text } from "@/components/text";
import { createStyles } from "@/theme";
export type Drawer01Props = DrawerContentProps & {
/** Runs when Log out is pressed. The drawer stays open; sign the user out here. */
onLogOut?: () => void;
};
export function Drawer01({ onLogOut, ...props }: Drawer01Props) {
const styles = useStyles();
return (
<DrawerContent {...props}>
<DrawerHeader style={styles.header}>
<View style={styles.profile}>
<Avatar fallback="JD" />
<View style={styles.identity}>
<Text variant="h4" numberOfLines={1}>
Jane Doe
</Text>
<Text variant="small" color="mutedForeground" numberOfLines={1}>
jane@example.com
</Text>
</View>
<Badge label="Pro" />
</View>
</DrawerHeader>
<DrawerSection title="Main">
<DrawerItem label="Home" icon={House} href="/" />
<DrawerItem label="Inbox" icon={Inbox} href="/inbox" badge={12} />
<DrawerItem label="Analytics" icon={BarChart3} href="/analytics" />
</DrawerSection>
<DrawerSection title="Workspace">
<DrawerItem label="Projects" icon={FolderKanban} href="/projects" />
<DrawerItem label="Team" icon={Users} href="/team" />
</DrawerSection>
<DrawerSection title="Support">
<DrawerItem label="Settings" icon={Settings} href="/settings" />
<DrawerItem label="Help" icon={LifeBuoy} href="/help" />
</DrawerSection>
<DrawerFooter>
<Separator />
<DrawerItem label="Log out" icon={LogOut} onPress={onLogOut} />
</DrawerFooter>
</DrawerContent>
);
}
const useStyles = createStyles((t) => ({
header: {
borderBottomWidth: t.borderWidth.default,
borderBottomColor: t.colors.border,
},
profile: { flexDirection: "row", alignItems: "center", gap: t.spacing[3] },
identity: { flex: 1, minWidth: 0 },
}));
