@ippis/app-layout-theme
v1.0.2
Published
A modern React component library with AppShell, built with TypeScript, Tailwind CSS, and Radix UI
Maintainers
Readme
@ippis/app-layout-theme
A modern, feature-rich React component library built with TypeScript, Tailwind CSS, and Radix UI. This library provides a complete application shell (AppShell) with sidebar navigation, top bar, user menu, and more.
Features
- 🎨 Modern Design: Built with Tailwind CSS and Radix UI primitives
- 📱 Responsive: Mobile-first design with responsive sidebar
- 🌓 Theme Support: Built-in light/dark mode toggle
- ♿ Accessible: Built on Radix UI for full accessibility support
- 🔧 TypeScript: Fully typed for better developer experience
- 🎯 Customizable: Highly configurable with props and theming options
Installation
npm install @ippis/app-layout-theme
# or
yarn add @ippis/app-layout-theme
# or
pnpm add @ippis/app-layout-themePeer Dependencies
This library requires the following peer dependencies:
npm install react react-dom react-router-domQuick Start
1. Install Tailwind CSS
This library uses Tailwind CSS. Make sure you have Tailwind configured in your project:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p2. Configure Tailwind
Add the library's styles to your tailwind.config.js:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/@ippis/app-layout-theme/dist/**/*.{js,ts,jsx,tsx}", // Add this
],
theme: {
extend: {},
},
plugins: [],
}3. Import Styles
Import the library's CSS in your main entry file:
// src/main.tsx or src/index.tsx
import "@ippis/app-layout-theme/dist/style.css";
// or if using a CSS bundler
import "@ippis/app-layout-theme/dist/index.css";4. Use the AppShell Component
import { AppShell, ShellMenuItem } from "@ippis/app-layout-theme";
import { LayoutDashboard, Users, Settings } from "lucide-react";
import { BrowserRouter } from "react-router-dom";
function App() {
const menus: ShellMenuItem[] = [
{
id: "dashboard",
label: "Dashboard",
icon: LayoutDashboard,
to: "/dashboard",
},
{
id: "users",
label: "Users",
icon: Users,
to: "/users",
},
{
id: "settings",
label: "Settings",
icon: Settings,
to: "/settings",
},
];
return (
<BrowserRouter>
<AppShell
title="My App"
subtitle="Application Description"
menus={menus}
user={{
name: "John Doe",
email: "[email protected]",
}}
theme={{ initialMode: "light" }}
>
<div className="p-6">
<h1>Welcome to My App</h1>
{/* Your app content */}
</div>
</AppShell>
</BrowserRouter>
);
}
export default App;API Reference
AppShell
The main component that provides the application shell layout with sidebar, top bar, and footer.
Props
All available properties for the AppShell component:
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| children | ReactNode | ✅ Yes | - | Main content area rendered inside the shell |
| menus | ShellMenuItem[] | ✅ Yes | - | Array of menu items for sidebar navigation. See ShellMenuItem type below. |
| title | string | No | - | Application title displayed in the sidebar header |
| subtitle | string | No | - | Application subtitle displayed below the title |
| logo | ReactNode | No | - | Custom logo component to display in the sidebar. Can be an image, SVG, or any React component |
| user | ShellUser | No | - | User information object for the user menu in the top bar. See ShellUser type below |
| search | ShellSearchConfig | No | - | Search configuration object. When provided, enables the search bar in the top bar. See ShellSearchConfig type below |
| showSearch | boolean | No | true | Show or hide the search bar. Set to false to hide search even if search prop is provided |
| institutions | ShellInstitution[] | No | - | Array of institution objects for the institution selector dropdown. See ShellInstitution type below |
| selectedInstitutionId | string | No | - | ID of the currently selected institution. Must match one of the IDs in the institutions array |
| onInstitutionChange | (institutionId: string) => void | No | - | Callback function called when user selects a different institution. Receives the new institution ID |
| institutionPlaceholder | string | No | - | Placeholder text for the institution selector when no institution is selected |
| showInstitutionSelector | boolean | No | true | Show or hide the institution selector in the top bar |
| quickActions | QuickAction[] | No | - | Array of quick action buttons displayed in the top bar. See QuickAction type below |
| appLauncherItems | AppLauncherItem[] | No | - | Array of items for the app launcher menu. See AppLauncherItem type below |
| sidebarHeader | ReactNode | No | - | Custom content to display in the sidebar header. Overrides the default header with title/subtitle |
| sidebarFooter | ReactNode | No | - | Custom content to display in the sidebar footer (below menu items) |
| footer | ReactNode | No | - | Custom footer content. Note: Use footerContent instead for better control |
| footerContent | ReactNode | No | - | Custom footer content that replaces the default footer. If not provided, shows default footer with copyright and links |
| showFooter | boolean | No | true | Show or hide the footer at the bottom of the shell |
| pagination | PaginationConfig | No | - | Pagination configuration object. When provided, displays pagination controls above the footer. See PaginationConfig type below |
| theme | ShellThemeOptions | No | - | Theme configuration object for light/dark mode. See ShellThemeOptions type below |
| actions | ReactNode | No | - | Custom action buttons or components to display in the top bar (next to quick actions) |
| linkComponent | ComponentType<ShellLinkComponentProps> | No | - | Custom link component for menu navigation. Useful if you're using a different routing library. See ShellLinkComponentProps type below |
| onMenuSelect | (item: ShellMenuItem) => void | No | - | Callback function called when a menu item is clicked. Receives the clicked menu item object |
| className | string | No | - | Additional CSS classes to apply to the root AppShell container |
Type Definitions
ShellMenuItem
Menu item configuration for sidebar navigation. Supports nested menus with children.
type ShellMenuItem = {
id: string; // Unique identifier for the menu item (required)
label: string; // Display text for the menu item (required)
description?: string; // Optional description text shown below the label
icon?: ComponentType<{ className?: string }>; // Optional icon component (e.g., from lucide-react)
to?: string; // React Router path for internal navigation
href?: string; // External URL for external links
onSelect?: () => void; // Callback function called when item is clicked
badge?: ReactNode; // Optional badge component (e.g., notification count)
disabled?: boolean; // Disable the menu item (default: false)
children?: ShellMenuItem[]; // Nested menu items for sub-menus
meta?: Record<string, unknown>; // Optional metadata object for custom data
};Example:
const menus: ShellMenuItem[] = [
{
id: "dashboard",
label: "Dashboard",
icon: LayoutDashboard,
to: "/dashboard",
},
{
id: "settings",
label: "Settings",
icon: Settings,
children: [
{
id: "settings-profile",
label: "Profile",
to: "/settings/profile",
},
{
id: "settings-account",
label: "Account",
to: "/settings/account",
},
],
},
];ShellUser
User information object for the user menu in the top bar.
type ShellUser = {
name: string; // User's full name (required)
email?: string; // User's email address
avatarUrl?: string; // URL to user's avatar image
extras?: ReactNode; // Additional content to display in user menu header
menuItems?: ShellUserMenuItem[]; // Custom menu items for the user dropdown
};Example:
const user: ShellUser = {
name: "John Doe",
email: "[email protected]",
avatarUrl: "/avatars/john.jpg",
menuItems: [
{
id: "profile",
label: "Profile",
icon: User,
href: "/profile",
},
{
id: "logout",
label: "Log out",
icon: LogOut,
onSelect: () => handleLogout(),
danger: true,
},
],
};ShellUserMenuItem
Menu item for the user dropdown menu.
type ShellUserMenuItem = {
id: string; // Unique identifier (required)
label: string; // Display text (required)
icon?: ComponentType<{ className?: string }>; // Optional icon
description?: string; // Optional description
href?: string; // External URL
onSelect?: () => void; // Callback when clicked
danger?: boolean; // Style as dangerous action (red text)
};ShellSearchConfig
Configuration object for the search functionality.
type ShellSearchConfig = {
placeholder?: string; // Placeholder text for search input (default: "Search...")
value?: string; // Controlled search value
defaultValue?: string; // Uncontrolled default value
debounceMs?: number; // Debounce delay for onChange in milliseconds
onChange?: (value: string) => void; // Callback when search value changes
onSubmit?: (value: string) => void; // Callback when user submits search (Enter key)
render?: (props: ShellSearchRenderProps) => ReactNode; // Custom render function
};Example:
const searchConfig: ShellSearchConfig = {
placeholder: "Search modules, actions or pages",
value: searchTerm,
onChange: (value) => setSearchTerm(value),
onSubmit: (value) => {
console.log("Searching for:", value);
// Perform search
},
debounceMs: 300,
};ShellInstitution
Institution object for the institution selector.
type ShellInstitution = {
id: string; // Unique identifier (required)
name: string; // Full institution name (required)
acronym: string; // Short acronym (required)
};Example:
const institutions: ShellInstitution[] = [
{
id: "1",
name: "Ministry of Public Service and Labour",
acronym: "MIFOTRA",
},
{
id: "2",
name: "Ministry of Finance and Economic Planning",
acronym: "MINECOFIN",
},
];QuickAction
Quick action button configuration for the top bar.
type QuickAction = {
id: string; // Unique identifier (required)
icon: ComponentType<{ className?: string }>; // Icon component (required)
label?: string; // Optional label text
tooltip?: string; // Tooltip text shown on hover
onSelect?: () => void; // Callback when clicked
panel?: ReactNode; // Panel content shown when action is clicked (sheet/modal)
variant?: "default" | "primary"; // Visual variant (default: "default")
};Example:
const quickActions: QuickAction[] = [
{
id: "notifications",
icon: Bell,
label: "Notifications",
tooltip: "View notifications",
panel: <NotificationsPanel />,
},
{
id: "help",
icon: HelpCircle,
label: "Help",
tooltip: "Get help",
panel: <HelpPanel />,
variant: "primary",
},
];AppLauncherItem
Item for the app launcher menu.
type AppLauncherItem = {
id: string; // Unique identifier (required)
label: string; // Display text (required)
icon?: ComponentType<{ className?: string }>; // Optional icon
description?: string; // Optional description
href?: string; // URL to navigate to
onSelect?: () => void; // Callback when clicked
};Example:
const appLauncherItems: AppLauncherItem[] = [
{
id: "dashboard",
label: "Dashboard",
icon: LayoutDashboard,
href: "/dashboard",
},
{
id: "analytics",
label: "Analytics",
icon: BarChart,
description: "View analytics and reports",
href: "/analytics",
},
];PaginationConfig
Pagination configuration object.
type PaginationConfig = {
page: number; // Current page number (1-indexed)
totalPages: number; // Total number of pages
onChange: (page: number) => void; // Callback when page changes
label?: string; // Optional label text
pageSize?: number; // Current page size
pageSizeOptions?: number[]; // Available page sizes
onPageSizeChange?: (size: number) => void; // Callback when size changes
totalItems?: number; // Total number of records
showNumbers?: boolean; // Toggle numbered buttons
isLoading?: boolean; // Disable controls + show spinner
};Example:
const pagination: PaginationConfig = {
page: currentPage,
totalPages,
onChange: (page) => {
setCurrentPage(page);
fetchData({ page, size: pageSize });
},
label: "Applications",
pageSize,
pageSizeOptions: [10, 25, 50],
onPageSizeChange: (size) => {
setPageSize(size);
fetchData({ page: 1, size });
},
totalItems,
isLoading,
};Using useShellPagination
For most screens, you can register pagination controls via the useShellPagination
hook instead of manually passing pagination props to AppShell.
import { useEffect, useState } from "react";
import { useShellPagination } from "@/theme/shell";
export const ApplicationsScreen = () => {
const [rows, setRows] = useState([]);
const {
page,
pageSize,
setTotalItems,
setLoading,
isLoading,
} = useShellPagination({
initialPageSize: 20,
pageSizeOptions: [10, 20, 50],
onPageChange: (nextPage, size) => {
fetchApplications(nextPage, size);
},
onPageSizeChange: (_, size) => {
fetchApplications(1, size);
},
});
const fetchApplications = async (nextPage = page, size = pageSize) => {
setLoading(true);
try {
const response = await api.getApplications({ page: nextPage, size });
setRows(response.items);
setTotalItems(response.total);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchApplications(page, pageSize);
}, [page, pageSize]);
return <ApplicationsTable data={rows} loading={isLoading} />;
};Key tips:
- Mounting
useShellPaginationautomatically renders the footer controls; unmounting removes them. - Call
setTotalItemswhen the backend returns the total count so the footer can display the range. - Use
setLoading(or pass theloadingoption) to show the spinner and temporarily disable controls during fetches.
ShellThemeOptions
Theme configuration for light/dark mode.
type ShellThemeOptions = {
initialMode?: "light" | "dark"; // Initial theme mode (default: "light")
onModeChange?: (mode: "light" | "dark") => void; // Callback when theme changes
};Example:
const theme: ShellThemeOptions = {
initialMode: "dark",
onModeChange: (mode) => {
console.log("Theme changed to:", mode);
// Save theme preference
localStorage.setItem("theme", mode);
},
};ShellLinkComponentProps
Props for custom link components.
type ShellLinkComponentProps = {
to?: string; // React Router path
href?: string; // External URL
className?: string; // CSS classes
children: ReactNode; // Link content (required)
onClick?: (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) => void; // Click handler
};Example:
// Custom link component for Next.js
const CustomLink = ({ to, href, className, children, onClick }: ShellLinkComponentProps) => {
if (to) {
return (
<Link href={to} className={className} onClick={onClick}>
{children}
</Link>
);
}
return (
<a href={href} className={className} onClick={onClick}>
{children}
</a>
);
};Examples
Complete Example with All Properties
Here's a comprehensive example showing all available properties:
import { useState } from "react";
import { AppShell, ShellMenuItem } from "@ippis/app-layout-theme";
import {
LayoutDashboard,
Users,
Settings,
FileText,
Bell,
HelpCircle,
User,
LogOut,
BarChart
} from "lucide-react";
import { BrowserRouter } from "react-router-dom";
import { Button } from "@/components/ui/button"; // Adjust import path as needed
function App() {
const [searchTerm, setSearchTerm] = useState("");
const [currentPage, setCurrentPage] = useState(1);
const [selectedInstitution, setSelectedInstitution] = useState("1");
// Menu configuration
const menus: ShellMenuItem[] = [
{
id: "dashboard",
label: "Dashboard",
icon: LayoutDashboard,
to: "/dashboard",
badge: "New",
},
{
id: "users",
label: "Users",
icon: Users,
to: "/users",
children: [
{
id: "users-active",
label: "Active Users",
to: "/users/active",
},
{
id: "users-pending",
label: "Pending",
to: "/users/pending",
badge: 5,
},
],
},
{
id: "documents",
label: "Documents",
icon: FileText,
to: "/documents",
},
{
id: "settings",
label: "Settings",
icon: Settings,
to: "/settings",
disabled: false,
},
];
// Institution configuration
const institutions = [
{
id: "1",
name: "Ministry of Public Service and Labour",
acronym: "MIFOTRA",
},
{
id: "2",
name: "Ministry of Finance and Economic Planning",
acronym: "MINECOFIN",
},
];
// User configuration
const user = {
name: "John Doe",
email: "[email protected]",
avatarUrl: "/avatars/john.jpg",
menuItems: [
{
id: "profile",
label: "Profile",
icon: User,
href: "/profile",
},
{
id: "logout",
label: "Log out",
icon: LogOut,
onSelect: () => {
console.log("Logging out...");
// Handle logout
},
danger: true,
},
],
};
// Quick actions configuration
const quickActions = [
{
id: "notifications",
icon: Bell,
label: "Notifications",
tooltip: "View notifications",
panel: (
<div className="p-4 space-y-2">
<h3 className="font-semibold mb-2">Notifications</h3>
<div className="space-y-2">
<div className="p-2 bg-slate-100 dark:bg-slate-800 rounded">
<p className="text-sm">New message received</p>
<p className="text-xs text-slate-500">2 minutes ago</p>
</div>
<div className="p-2 bg-slate-100 dark:bg-slate-800 rounded">
<p className="text-sm">Task assigned to you</p>
<p className="text-xs text-slate-500">1 hour ago</p>
</div>
</div>
</div>
),
},
{
id: "help",
icon: HelpCircle,
label: "Help",
tooltip: "Get help and support",
panel: (
<div className="p-4">
<h3 className="font-semibold mb-2">Help & Support</h3>
<p className="text-sm text-slate-600 dark:text-slate-400">
Need assistance? Contact our support team or check the documentation.
</p>
<Button className="mt-4" variant="outline">
Contact Support
</Button>
</div>
),
variant: "primary" as const,
},
];
// App launcher items
const appLauncherItems = [
{
id: "dashboard",
label: "Dashboard",
icon: LayoutDashboard,
href: "/dashboard",
},
{
id: "analytics",
label: "Analytics",
icon: BarChart,
description: "View analytics",
href: "/analytics",
},
];
return (
<BrowserRouter>
<AppShell
// Required props
menus={menus}
// Basic configuration
title="My Application"
subtitle="Application Description"
logo={<img src="/logo.png" alt="Logo" />}
// User configuration
user={user}
// Search configuration
search={{
placeholder: "Search modules, actions or pages",
value: searchTerm,
onChange: setSearchTerm,
onSubmit: (value) => {
console.log("Searching for:", value);
// Perform search
},
debounceMs: 300,
}}
showSearch={true}
// Institution selector
institutions={institutions}
selectedInstitutionId={selectedInstitution}
onInstitutionChange={(id) => {
setSelectedInstitution(id);
console.log("Institution changed to:", id);
}}
institutionPlaceholder="Select institution"
showInstitutionSelector={true}
// Quick actions
quickActions={quickActions}
// App launcher
appLauncherItems={appLauncherItems}
// Custom sidebar content
sidebarHeader={
<div className="p-4 border-b border-slate-200 dark:border-slate-800">
<div className="flex items-center gap-2">
<div className="w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center text-white font-bold">
ALT
</div>
<div>
<h2 className="font-semibold text-sm">App Layout Theme</h2>
<p className="text-xs text-slate-500">Custom Header</p>
</div>
</div>
</div>
}
sidebarFooter={
<div className="p-4 space-y-2">
<Button
variant="outline"
size="sm"
className="w-full justify-center gap-2"
onClick={() => window.open("https://github.com", "_blank")}
>
<HelpCircle className="h-4 w-4" />
Support
</Button>
</div>
}
// Footer configuration
footerContent={
<footer className="text-sm text-center py-3 text-slate-600 dark:text-slate-400">
© 2025 @ippis/app-layout-theme |{" "}
<a
href="#"
className="hover:underline text-blue-600 dark:text-blue-400"
onClick={(e) => {
e.preventDefault();
alert("Privacy policy");
}}
>
Privacy
</a>{" "}
|{" "}
<a
href="#"
className="hover:underline text-blue-600 dark:text-blue-400"
onClick={(e) => {
e.preventDefault();
alert("Terms of service");
}}
>
Terms
</a>
</footer>
}
showFooter={true}
// Pagination
pagination={{
page: currentPage,
totalPages: 10,
onChange: (page) => {
setCurrentPage(page);
// Fetch data for new page
},
label: "Page",
}}
// Theme configuration
theme={{
initialMode: "light",
onModeChange: (mode) => {
console.log("Theme changed to:", mode);
localStorage.setItem("theme", mode);
},
}}
// Custom actions
actions={
<Button
variant="secondary"
size="sm"
onClick={() => alert("Custom action clicked!")}
>
Quick Action
</Button>
}
// Menu callbacks
onMenuSelect={(item) => {
console.log("Menu item selected:", item);
}}
// Styling
className="custom-app-shell"
>
<div className="p-6 space-y-6">
<div>
<h1 className="text-3xl font-bold mb-2">Welcome to My Application</h1>
<p className="text-slate-600 dark:text-slate-400">
This is a comprehensive demonstration of all AppShell properties.
The AppShell component provides a complete application shell with sidebar,
top bar, user menu, search, and more.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="p-6 bg-white dark:bg-slate-800 rounded-lg shadow border border-slate-200 dark:border-slate-700">
<div className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center">
<LayoutDashboard className="h-5 w-5 text-blue-600 dark:text-blue-400" />
</div>
<h2 className="text-xl font-semibold">Dashboard</h2>
</div>
<p className="text-slate-600 dark:text-slate-400">
View your application dashboard with key metrics and insights.
</p>
</div>
<div className="p-6 bg-white dark:bg-slate-800 rounded-lg shadow border border-slate-200 dark:border-slate-700">
<div className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center">
<Users className="h-5 w-5 text-green-600 dark:text-green-400" />
</div>
<h2 className="text-xl font-semibold">User Management</h2>
</div>
<p className="text-slate-600 dark:text-slate-400">
Manage users, roles, and permissions with ease.
</p>
</div>
<div className="p-6 bg-white dark:bg-slate-800 rounded-lg shadow border border-slate-200 dark:border-slate-700">
<div className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 bg-purple-100 dark:bg-purple-900 rounded-lg flex items-center justify-center">
<FileText className="h-5 w-5 text-purple-600 dark:text-purple-400" />
</div>
<h2 className="text-xl font-semibold">Documents</h2>
</div>
<p className="text-slate-600 dark:text-slate-400">
Access and manage your documents and files.
</p>
</div>
</div>
<div className="p-6 bg-white dark:bg-slate-800 rounded-lg shadow border border-slate-200 dark:border-slate-700">
<h2 className="text-xl font-semibold mb-4">Features Demonstrated</h2>
<ul className="space-y-2 text-slate-600 dark:text-slate-400">
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Sidebar navigation with nested menus and badges
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Institution selector in the top bar
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Search functionality with debouncing
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
User menu with custom menu items
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Quick actions with panels (notifications, help)
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
App launcher menu
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Pagination controls
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Light/dark theme toggle
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Custom sidebar header and footer
</li>
<li className="flex items-center gap-2">
<span className="w-2 h-2 bg-blue-600 rounded-full"></span>
Custom footer content
</li>
</ul>
</div>
</div>
</AppShell>
</BrowserRouter>
);
}
export default App;Basic Usage
import { AppShell } from "@ippis/app-layout-theme";
<AppShell
title="My App"
menus={menus}
user={{ name: "John Doe", email: "[email protected]" }}
>
<div>Your content here</div>
</AppShell>With Custom Logo
<AppShell
title="My App"
logo={<img src="/logo.png" alt="Logo" />}
menus={menus}
>
<div>Content</div>
</AppShell>With Institution Selector
<AppShell
title="My App"
menus={menus}
institutions={[
{ id: "1", name: "Organization 1", acronym: "ORG1" },
{ id: "2", name: "Organization 2", acronym: "ORG2" },
]}
selectedInstitutionId="1"
onInstitutionChange={(id) => console.log("Changed to:", id)}
>
<div>Content</div>
</AppShell>With Search
<AppShell
title="My App"
menus={menus}
search={{
placeholder: "Search...",
value: searchTerm,
onChange: setSearchTerm,
onSubmit: (value) => {
console.log("Searching for:", value);
},
}}
>
<div>Content</div>
</AppShell>With Quick Actions
import { Bell, HelpCircle } from "lucide-react";
<AppShell
title="My App"
menus={menus}
quickActions={[
{
id: "notifications",
icon: Bell,
label: "Notifications",
tooltip: "View notifications",
panel: <NotificationsPanel />,
},
{
id: "help",
icon: HelpCircle,
label: "Help",
tooltip: "Get help",
panel: <HelpPanel />,
},
]}
>
<div>Content</div>
</AppShell>With Pagination
<AppShell
title="My App"
menus={menus}
pagination={{
page: currentPage,
totalPages: 10,
onChange: (page) => setCurrentPage(page),
}}
>
<div>Content</div>
</AppShell>Custom Link Component
If you're using a different routing library:
import { Link } from "your-router-library";
const CustomLink = ({ to, href, className, children, onClick }) => {
if (to) {
return (
<Link to={to} className={className} onClick={onClick}>
{children}
</Link>
);
}
return (
<a href={href} className={className} onClick={onClick}>
{children}
</a>
);
};
<AppShell
title="My App"
menus={menus}
linkComponent={CustomLink}
>
<div>Content</div>
</AppShell>Styling
The library uses Tailwind CSS for styling. You can customize the appearance by:
- CSS Variables: Override CSS variables in your global CSS:
:root {
--primary: 226.6667 66.0550% 42.7451%;
--primary-foreground: 0 0% 100%;
/* ... other variables */
}- Tailwind Config: Extend the theme in your
tailwind.config.js:
export default {
theme: {
extend: {
colors: {
primary: "hsl(var(--primary))",
// ... other colors
},
},
},
}TypeScript
The library is fully typed. Import types as needed:
import type {
ShellMenuItem,
ShellUser,
ShellSearchConfig,
AppShellProps,
} from "@ippis/app-layout-theme";Browser Support
- Chrome (last 2 versions)
- Firefox (last 2 versions)
- Safari (last 2 versions)
- Edge (last 2 versions)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT
Publishing
If you want to publish this library to npm, see PUBLISHING.md for detailed instructions.
Quick steps:
- Update
package.jsonwith your repository URLs - Build the library:
npm run build:lib - Test:
npm publish --dry-run - Publish:
npm publish
Support
For issues and questions, please open an issue on the GitHub repository.
