Overview
The AppShell component is the root-level provider component for Tailor Platform applications. It manages application-wide context including navigation configuration, theme, authentication state, and custom context data.
This component should be mounted at the root of your application (catch-all segment in Next.js App Router, or root in Vite/React apps).
Props
App shell title displayed in the sidebar header
App shell icon displayed in the sidebar header
Base path for the app shell routing
Navigation configuration created with defineModule(). When using the vite-plugin, this is automatically set via AppShell.WithPages().
A component to be rendered at the root level of AppShell (path=”/”). Use guards with redirectTo() for redirects.rootComponent: () => <DashboardHome />
Settings resources that appear only in the Settings menu
Locale code for built-in UI strings (e.g., “en”, “ja”, “fr”). Built-in translations available for “en” and “ja” only. Auto-detects from browser preferences if not provided, defaults to “en” if no browser locale is available.
Global error boundary component applied to all routes. When an error occurs in any route component, this component will render. Module and resource-level error boundaries take precedence over this.Use the useRouteError hook to access error details within the component.import { useRouteError } from "@tailor-platform/app-shell";
const GlobalErrorBoundary = () => {
const error = useRouteError() as Error;
return (
<div>
<h1>Something went wrong</h1>
<p>{error.message}</p>
</div>
);
};
<AppShell
modules={[...]}
errorBoundary={<GlobalErrorBoundary />}
/>
Custom context data accessible from guards and components. Use module augmentation to define the type of context data.// types.d.ts
declare module "@tailor-platform/app-shell" {
interface AppShellRegister {
contextData: {
apiClient: ApiClient;
currentUser: User;
};
}
}
// App.tsx
<AppShell
modules={modules}
contextData={{ apiClient, currentUser }}
/>
Layout components like SidebarLayout
Route Configuration
Routes can be configured in two ways:
1. Automatic (Recommended)
Use the vite-plugin which automatically configures pages via AppShell.WithPages():
import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
<AppShell title="My App">
<SidebarLayout />
</AppShell>
2. Explicit Modules
Pass the modules prop for manual configuration:
import { AppShell, defineModule, SidebarLayout } from "@tailor-platform/app-shell";
const modules = [
defineModule({
path: "dashboard",
meta: { title: "Dashboard" },
component: DashboardPage,
resources: [...]
})
];
<AppShell title="My App" modules={modules}>
<SidebarLayout />
</AppShell>
Usage Examples
Basic Setup
import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
const App = () => (
<AppShell title="My Application">
<SidebarLayout />
</AppShell>
);
With Custom Icon and Locale
import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
import { Building } from "lucide-react";
const App = () => (
<AppShell
title="ERP System"
icon={<Building />}
locale="en"
>
<SidebarLayout />
</AppShell>
);
With Context Data
import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
const App = () => {
const apiClient = useApiClient();
const currentUser = useCurrentUser();
return (
<AppShell
title="My Application"
contextData={{
apiClient,
currentUser,
}}
>
<SidebarLayout />
</AppShell>
);
};
With Root Component
import { AppShell, SidebarLayout } from "@tailor-platform/app-shell";
const HomePage = () => <div>Welcome Home!</div>;
const App = () => (
<AppShell
title="My App"
rootComponent={() => <HomePage />}
>
<SidebarLayout />
</AppShell>
);
With Error Boundary
import { AppShell, SidebarLayout, useRouteError } from "@tailor-platform/app-shell";
const GlobalErrorBoundary = () => {
const error = useRouteError() as Error;
return (
<div className="error-container">
<h1>Oops! Something went wrong</h1>
<p>{error.message}</p>
<button onClick={() => window.location.reload()}>
Reload Page
</button>
</div>
);
};
const App = () => (
<AppShell
title="My Application"
errorBoundary={<GlobalErrorBoundary />}
>
<SidebarLayout />
</AppShell>
);
Complete Configuration
import {
AppShell,
SidebarLayout,
defineModule,
defineResource
} from "@tailor-platform/app-shell";
import { Package, Settings } from "lucide-react";
const modules = [
defineModule({
path: "products",
meta: {
title: "Products",
icon: <Package />
},
component: ProductsPage,
resources: [
defineResource({
path: "all",
meta: { title: "All Products" },
component: AllProductsPage
})
]
})
];
const settingsResources = [
defineResource({
path: "general",
meta: { title: "General", icon: <Settings /> },
component: GeneralSettingsPage
})
];
const App = () => {
const apiClient = useApiClient();
const currentUser = useCurrentUser();
return (
<AppShell
title="ERP System"
icon={<Building />}
basePath="app"
modules={modules}
settingsResources={settingsResources}
locale="en"
contextData={{ apiClient, currentUser }}
>
<SidebarLayout />
</AppShell>
);
};
Context Access
Access AppShell context in your components using the provided hooks:
import { useAppShell, useAppShellConfig, useAppShellData } from "@tailor-platform/app-shell";
// Access all context
const { title, configurations, navItems, contextData } = useAppShell();
// Access only config
const { title, basePath, modules } = useAppShellConfig();
// Access only custom context data
const contextData = useAppShellData();
TypeScript
AppShellProps Type
type AppShellProps = {
title?: string;
icon?: React.ReactNode;
basePath?: string;
modules?: Modules;
rootComponent?: () => React.ReactNode;
settingsResources?: Array<Resource>;
locale?: string;
errorBoundary?: ErrorBoundaryComponent;
contextData?: ContextData;
children?: React.ReactNode;
};
Context Data Type Augmentation
// types/app-shell.d.ts
declare module "@tailor-platform/app-shell" {
interface AppShellRegister {
contextData: {
apiClient: ApiClient;
currentUser: User | null;
featureFlags: Record<string, boolean>;
};
}
}
See Also