From 98db0c29d2ef5df2175a79049360897224e02bd1 Mon Sep 17 00:00:00 2001
From: Prakhar Gupta <92228082+prakhargupta1@users.noreply.github.com>
Date: Wed, 13 Nov 2024 11:37:16 +0300
Subject: [PATCH 1/6] docs
---
.../ReactRouter.js | 0
.../ReactRouter.tsx | 0
.../ReactRouter.tsx.preview | 0
.../core/integrations/nextjs-approuter.md | 233 ++++
.../core/integrations/nextjs-pagesrouter.md | 414 +++++++
.../toolpad/core/integrations/react-router.md | 399 +++++++
.../toolpad/core/introduction/integration.md | 1062 -----------------
docs/data/toolpad/core/pages.ts | 22 +-
.../core/integrations/nextjs-approuter.js | 7 +
.../core/integrations/nextjs-pagesrouter.js | 7 +
.../react-router.js} | 2 +-
11 files changed, 1079 insertions(+), 1067 deletions(-)
rename docs/data/toolpad/core/{introduction => integrations}/ReactRouter.js (100%)
rename docs/data/toolpad/core/{introduction => integrations}/ReactRouter.tsx (100%)
rename docs/data/toolpad/core/{introduction => integrations}/ReactRouter.tsx.preview (100%)
create mode 100644 docs/data/toolpad/core/integrations/nextjs-approuter.md
create mode 100644 docs/data/toolpad/core/integrations/nextjs-pagesrouter.md
create mode 100644 docs/data/toolpad/core/integrations/react-router.md
delete mode 100644 docs/data/toolpad/core/introduction/integration.md
create mode 100644 docs/pages/toolpad/core/integrations/nextjs-approuter.js
create mode 100644 docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
rename docs/pages/toolpad/core/{introduction/integration.js => integrations/react-router.js} (65%)
diff --git a/docs/data/toolpad/core/introduction/ReactRouter.js b/docs/data/toolpad/core/integrations/ReactRouter.js
similarity index 100%
rename from docs/data/toolpad/core/introduction/ReactRouter.js
rename to docs/data/toolpad/core/integrations/ReactRouter.js
diff --git a/docs/data/toolpad/core/introduction/ReactRouter.tsx b/docs/data/toolpad/core/integrations/ReactRouter.tsx
similarity index 100%
rename from docs/data/toolpad/core/introduction/ReactRouter.tsx
rename to docs/data/toolpad/core/integrations/ReactRouter.tsx
diff --git a/docs/data/toolpad/core/introduction/ReactRouter.tsx.preview b/docs/data/toolpad/core/integrations/ReactRouter.tsx.preview
similarity index 100%
rename from docs/data/toolpad/core/introduction/ReactRouter.tsx.preview
rename to docs/data/toolpad/core/integrations/ReactRouter.tsx.preview
diff --git a/docs/data/toolpad/core/integrations/nextjs-approuter.md b/docs/data/toolpad/core/integrations/nextjs-approuter.md
new file mode 100644
index 00000000000..6513e571f1d
--- /dev/null
+++ b/docs/data/toolpad/core/integrations/nextjs-approuter.md
@@ -0,0 +1,233 @@
+---
+title: Next.js - Integration
+---
+
+# Next.js App Router
+
+
This guide walks you through adding Toolpad Core to an existing Next.js app.
+
+## Wrap your application with `AppProvider`
+
+In your root layout file (for example, `app/layout.tsx`), wrap your application with the `AppProvider`:
+
+```tsx title="app/layout.tsx"
+import { AppProvider } from '@toolpad/core/AppProvider';
+import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter';
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+ {
+ 'use server';
+ try {
+ return await signIn(provider.id, {
+ redirectTo: callbackUrl ?? '/',
+ });
+ } catch (error) {
+ // The desired flow for successful sign in in all cases
+ // and unsuccessful sign in for OAuth providers will cause a `redirect`,
+ // and `redirect` is a throwing function, so we need to re-throw
+ // to allow the redirect to happen
+ // Source: https://github.com/vercel/next.js/issues/49298#issuecomment-1542055642
+ // Detect a `NEXT_REDIRECT` error and re-throw it
+ if (error instanceof Error && error.message === 'NEXT_REDIRECT') {
+ throw error;
+ }
+ // Handle Auth.js errors
+ if (error instanceof AuthError) {
+ return {
+ error: error.message,
+ type: error.type,
+ };
+ }
+ // An error boundary must exist to handle unknown errors
+ return {
+ error: 'Something went wrong.',
+ type: 'UnknownError',
+ };
+ }
+ }}
+ />
+ );
+}
+```
+
+### Create a route handler for sign-in
+
+`next-auth` requires a route handler for sign-in. Create a file `app/api/auth/[...nextauth]/route.ts`:
+
+```ts title="app/api/auth/[...nextauth]/route.ts"
+import { handlers } from '../../../../auth';
+
+export const { GET, POST } = handlers;
+```
+
+### Add a middleware
+
+Add a middleware to your app to protect your dashboard pages:
+
+```ts title="middleware.ts"
+export { auth as middleware } from './auth';
+
+export const config = {
+ // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
+ matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
+};
+```
+
+That's it! You now have Toolpad Core integrated into your Next.js App Router app with authentication setup:
+
+{{"component": "modules/components/DocsImage.tsx", "src": "/static/toolpad/docs/core/integration-nextjs-app.png", "srcDark": "/static/toolpad/docs/core/integration-nextjs-app-dark.png", "alt": "Next.js App Router with Toolpad Core", "caption": "Next.js App Router with Toolpad Core", "zoom": true, "aspectRatio": "1.428" }}
+
+:::info
+For a full working example with authentication included, see the [Toolpad Core Next.js App with Auth.js example](https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs/)
+:::
diff --git a/docs/data/toolpad/core/integrations/nextjs-pagesrouter.md b/docs/data/toolpad/core/integrations/nextjs-pagesrouter.md
new file mode 100644
index 00000000000..69f5a4a2086
--- /dev/null
+++ b/docs/data/toolpad/core/integrations/nextjs-pagesrouter.md
@@ -0,0 +1,414 @@
+---
+title: Next.js - Integration
+---
+
+# Next.js Pages Router
+
+This guide walks you through adding Toolpad Core to an existing Next.js app.
+
+## Wrap your application with `AppProvider`
+
+In your root layout file (for example, `pages/_app.tsx`), wrap your application with the `AppProvider`:
+
+```tsx title="pages/_app.tsx"
+import * as React from 'react';
+import { AppProvider } from '@toolpad/core/nextjs';
+import { PageContainer } from '@toolpad/core/PageContainer';
+import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+import Head from 'next/head';
+import { AppCacheProvider } from '@mui/material-nextjs/v14-pagesRouter';
+import DashboardIcon from '@mui/icons-material/Dashboard';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
+import type { Navigation } from '@toolpad/core/AppProvider';
+
+const NAVIGATION: Navigation = [
+ {
+ kind: 'header',
+ title: 'Main items',
+ },
+ {
+ segment: '',
+ title: 'Dashboard',
+ icon: ,
+ },
+];
+
+const BRANDING = {
+ title: 'My Toolpad Core App',
+};
+
+export default function App({ Component }: { Component: React.ElementType }) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+:::info
+The `AppCacheProvider` component is not required to use Toolpad Core, but it's recommended.
+
+See the [Material UI Next.js Pages Router integration docs](https://mui.com/material-ui/integrations/nextjs/#configuration-2) for more details.
+:::
+
+## Modify `_document.tsx`
+
+Modify `_document.tsx` to include the `DocumentHeadTags` component:
+
+```tsx title="pages/_document.tsx"
+import * as React from 'react';
+import {
+ Html,
+ Head,
+ Main,
+ NextScript,
+ DocumentProps,
+ DocumentContext,
+} from 'next/document';
+import {
+ DocumentHeadTags,
+ DocumentHeadTagsProps,
+ documentGetInitialProps,
+} from '@mui/material-nextjs/v14-pagesRouter';
+
+export default function Document(props: DocumentProps & DocumentHeadTagsProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+Document.getInitialProps = async (ctx: DocumentContext) => {
+ const finalProps = await documentGetInitialProps(ctx);
+ return finalProps;
+};
+```
+
+## Add a dashboard page
+
+Create a dashboard page (for example, `pages/index.tsx`):
+
+```tsx title="pages/index.tsx"
+import * as React from 'react';
+import Typography from '@mui/material/Typography';
+
+export default function HomePage() {
+ return Welcome to Toolpad!;
+}
+```
+
+## (Optional) Add a second page
+
+Create a new page in the dashboard, for example, `pages/orders/index.tsx`:
+
+```tsx title="pages/orders/index.tsx"
+import * as React from 'react';
+import Typography from '@mui/material/Typography';
+
+export default function OrdersPage() {
+ return Welcome to the orders page!;
+}
+```
+
+To add this page to the navigation, add it to the `NAVIGATION` variable:
+
+```ts title="pages/_app.tsx"
+export const NAVIGATION = [
+ // ...
+ {
+ segment: 'orders',
+ title: 'Orders',
+ icon: ,
+ },
+ // ...
+];
+```
+
+## (Optional) Set up authentication
+
+If you want to add authentication, you can use Auth.js with Toolpad Core. Here's an example setup:
+
+### Install the dependencies
+
+```bash
+npm install next-auth@beta
+```
+
+### Create an `auth.ts` file
+
+```ts title="auth.ts"
+import NextAuth from 'next-auth';
+import GitHub from 'next-auth/providers/github';
+import type { Provider } from 'next-auth/providers';
+
+const providers: Provider[] = [
+ GitHub({
+ clientId: process.env.GITHUB_CLIENT_ID,
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
+ }),
+];
+
+export const providerMap = providers.map((provider) => {
+ if (typeof provider === 'function') {
+ const providerData = provider();
+ return { id: providerData.id, name: providerData.name };
+ }
+ return { id: provider.id, name: provider.name };
+});
+
+export const { handlers, auth } = NextAuth({
+ providers,
+ secret: process.env.AUTH_SECRET,
+ pages: {
+ signIn: '/auth/signin',
+ },
+ callbacks: {
+ authorized({ auth: session, request: { nextUrl } }) {
+ const isLoggedIn = !!session?.user;
+ const isPublicPage = nextUrl.pathname.startsWith('/public');
+
+ if (isPublicPage || isLoggedIn) {
+ return true;
+ }
+
+ return false; // Redirect unauthenticated users to login page
+ },
+ },
+});
+```
+
+### Modify `_app.tsx`
+
+Modify `_app.tsx` to include the `authentication` prop and other helpers:
+
+```tsx title="pages/_app.tsx"
+import * as React from 'react';
+import { AppProvider } from '@toolpad/core/nextjs';
+import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+import { PageContainer } from '@toolpad/core/PageContainer';
+import Head from 'next/head';
+import { AppCacheProvider } from '@mui/material-nextjs/v14-pagesRouter';
+import DashboardIcon from '@mui/icons-material/Dashboard';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
+import type { NextPage } from 'next';
+import type { AppProps } from 'next/app';
+import type { Navigation } from '@toolpad/core/AppProvider';
+import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react';
+import LinearProgress from '@mui/material/LinearProgress';
+
+export type NextPageWithLayout = NextPage
& {
+ getLayout?: (page: React.ReactElement) => React.ReactNode;
+ requireAuth?: boolean;
+};
+
+type AppPropsWithLayout = AppProps & {
+ Component: NextPageWithLayout;
+};
+
+const NAVIGATION: Navigation = [
+ {
+ kind: 'header',
+ title: 'Main items',
+ },
+ {
+ segment: '',
+ title: 'Dashboard',
+ icon: ,
+ },
+ {
+ segment: 'orders',
+ title: 'Orders',
+ icon: ,
+ },
+];
+
+const BRANDING = {
+ title: 'My Toolpad Core App',
+};
+
+const AUTHENTICATION = {
+ signIn,
+ signOut,
+};
+
+function getDefaultLayout(page: React.ReactElement) {
+ return (
+
+ {page}
+
+ );
+}
+
+function RequireAuth({ children }: { children: React.ReactNode }) {
+ const { status } = useSession();
+
+ if (status === 'loading') {
+ return ;
+ }
+
+ return children;
+}
+
+function AppLayout({ children }: { children: React.ReactNode }) {
+ const { data: session } = useSession();
+ return (
+
+
+
+
+
+ {children}
+
+
+ );
+}
+
+export default function App(props: AppPropsWithLayout) {
+ const {
+ Component,
+ pageProps: { session, ...pageProps },
+ } = props;
+
+ const getLayout = Component.getLayout ?? getDefaultLayout;
+ const requireAuth = Component.requireAuth ?? true;
+
+ let pageContent = getLayout();
+ if (requireAuth) {
+ pageContent = {pageContent};
+ }
+ pageContent = {pageContent};
+
+ return (
+
+ {pageContent}
+
+ );
+}
+```
+
+### Create a sign-in page
+
+Use the `SignInPage` component to add a sign-in page to your app. For example, `pages/auth/signin.tsx`:
+
+```tsx title="pages/auth/signin.tsx"
+import * as React from 'react';
+import type { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
+import Link from '@mui/material/Link';
+import { SignInPage } from '@toolpad/core/SignInPage';
+import { signIn } from 'next-auth/react';
+import { useRouter } from 'next/router';
+import { auth, providerMap } from '../../auth';
+
+export default function SignIn({
+ providers,
+}: InferGetServerSidePropsType) {
+ const router = useRouter();
+ return (
+ {
+ try {
+ const signInResponse = await signIn(provider.id, {
+ callbackUrl: callbackUrl ?? '/',
+ });
+ if (signInResponse && signInResponse.error) {
+ // Handle Auth.js errors
+ return {
+ error: signInResponse.error.message,
+ type: signInResponse.error,
+ };
+ }
+ return {};
+ } catch (error) {
+ // An error boundary must exist to handle unknown errors
+ return {
+ error: 'Something went wrong.',
+ type: 'UnknownError',
+ };
+ }
+ }}
+ />
+ );
+}
+
+SignIn.getLayout = (page: React.ReactNode) => page;
+
+SignIn.requireAuth = false;
+
+export async function getServerSideProps(context: GetServerSidePropsContext) {
+ const session = await auth(context);
+
+ // If the user is already logged in, redirect.
+ // Note: Make sure not to redirect to the same page
+ // To avoid an infinite loop!
+ if (session) {
+ return { redirect: { destination: '/' } };
+ }
+
+ return {
+ props: {
+ providers: providerMap,
+ },
+ };
+}
+```
+
+### Create a route handler for sign-in
+
+`next-auth` requires a route handler for sign-in. Create a file `app/api/auth/[...nextauth].ts`:
+
+```ts title="app/api/auth/[...nextauth].ts"
+import { handlers } from '../../../../auth';
+
+export const { GET, POST } = handlers;
+```
+
+:::warning
+
+Note that this file is a route handler and must be placed in the `app` directory, even if the rest of your app is in the `pages` directory. Know more in the [Auth.js documentation](https://authjs.dev/getting-started/installation#configure).
+
+:::
+
+#### Add a middleware
+
+Add a middleware to your app to protect your dashboard pages:
+
+```ts title="middleware.ts"
+export { auth as middleware } from './auth';
+
+export const config = {
+ // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
+ matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
+};
+```
+
+That's it! You now have Toolpad Core integrated into your Next.js Pages Router app with authentication setup:
+
+{{"component": "modules/components/DocsImage.tsx", "src": "/static/toolpad/docs/core/integration-nextjs-pages.png", "srcDark": "/static/toolpad/docs/core/integration-nextjs-pages-dark.png", "alt": "Next.js Pages Router with Toolpad Core", "caption": "Next.js Pages Router with Toolpad Core", "zoom": true, "aspectRatio": "1.428" }}
+
+:::info
+For a full working example with authentication included, see the [Toolpad Core Next.js Pages app with Auth.js example](https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs-pages/)
+:::
diff --git a/docs/data/toolpad/core/integrations/react-router.md b/docs/data/toolpad/core/integrations/react-router.md
new file mode 100644
index 00000000000..1b0dde51476
--- /dev/null
+++ b/docs/data/toolpad/core/integrations/react-router.md
@@ -0,0 +1,399 @@
+---
+title: React router - Integration
+---
+
+# React Router
+
+To integrate Toolpad Core into a single-page app (with Vite, for example) using React Router, follow these steps.
+
+## Wrap all your pages in an `AppProvider`
+
+In your router configuration (e.g.: `src/main.tsx`), use a shared component or element (e.g.: `src/App.tsx`) as a root **layout route** that will wrap the whole application with the `AppProvider` from `@toolpad/core/react-router-dom`.
+
+You must use the `` component from `react-router-dom` in this root layout element or component.
+
+```tsx title="src/main.tsx"
+import * as React from 'react';
+import * as ReactDOM from 'react-dom/client';
+import { createBrowserRouter, RouterProvider } from 'react-router-dom';
+import App from './App';
+import DashboardPage from './pages';
+import OrdersPage from './pages/orders';
+
+const router = createBrowserRouter([
+ {
+ Component: App, // root layout route
+ },
+]);
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+);
+```
+
+```tsx title="src/App.tsx"
+import * as React from 'react';
+import DashboardIcon from '@mui/icons-material/Dashboard';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
+import { AppProvider } from '@toolpad/core/react-router-dom';
+import { Outlet } from 'react-router-dom';
+import type { Navigation } from '@toolpad/core';
+
+const NAVIGATION: Navigation = [
+ {
+ kind: 'header',
+ title: 'Main items',
+ },
+ {
+ title: 'Dashboard',
+ icon: ,
+ },
+ {
+ segment: 'orders',
+ title: 'Orders',
+ icon: ,
+ },
+];
+
+const BRANDING = {
+ title: 'My Toolpad Core App',
+};
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+## Create a dashboard layout
+
+Create a layout file for your dashboard pages (e.g.: `src/layouts/dashboard.tsx`), to also be used as a layout route with the `` component from `react-router-dom`:
+
+```tsx title="src/layouts/dashboard.tsx"
+import * as React from 'react';
+import { Outlet } from 'react-router-dom';
+import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+import { PageContainer } from '@toolpad/core/PageContainer';
+
+export default function Layout() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+The [`DashboardLayout`](/toolpad/core/react-dashboard-layout/) component provides a consistent layout for your dashboard pages, including a sidebar, navigation, and header. The [`PageContainer`](/toolpad/core/react-page-container/) component is used to wrap the page content, and provides breadcrumbs for navigation.
+
+You can then add this layout component to your React Router configuration (e.g.: `src/main.tsx`), as a child of the root layout route created in step 1.
+
+```tsx title="src/main.tsx"
+import Layout from './layouts/dashboard';
+
+//...
+const router = createBrowserRouter([
+ {
+ Component: App, // root layout route
+ children: [
+ {
+ path: '/',
+ Component: Layout,
+ },
+ ],
+ },
+]);
+//...
+```
+
+## Create pages
+
+Create a dashboard page (e.g.: `src/pages/index.tsx`) and an orders page (`src/pages/orders.tsx`).
+
+```tsx title="src/pages/index.tsx"
+import * as React from 'react';
+import Typography from '@mui/material/Typography';
+
+export default function DashboardPage() {
+ return Welcome to Toolpad!;
+}
+```
+
+```tsx title="src/pages/orders.tsx"
+import * as React from 'react';
+import Typography from '@mui/material/Typography';
+
+export default function OrdersPage() {
+ return Welcome to the Toolpad orders!;
+}
+```
+
+You can then add these page components as routes to your React Router configuration (e.g.: `src/main.tsx`). By adding them as children of the layout route created in step 2, they will automatically be wrapped with that dashboard layout:
+
+```tsx title="src/main.tsx"
+import DashboardPage from './pages';
+import OrdersPage from './pages/orders';
+
+//...
+const router = createBrowserRouter([
+ {
+ Component: App, // root layout route
+ children: [
+ {
+ path: '/',
+ Component: Layout,
+ children: [
+ {
+ path: '',
+ Component: DashboardPage,
+ },
+ {
+ path: 'orders',
+ Component: OrdersPage,
+ },
+ ],
+ },
+ ],
+ },
+]);
+//...
+```
+
+That's it! You now have Toolpad Core integrated into your single-page app with React Router!
+
+{{"demo": "ReactRouter.js", "height": 500, "iframe": true, "hideToolbar": true}}
+
+:::info
+For a full working example, see the [Toolpad Core Vite app with React Router example](https://github.com/mui/toolpad/tree/master/examples/core/vite/)
+:::
+
+## (Optional) Set up authentication
+
+You can use the `SignInPage` component to add authentication along with an external authentication provider of your choice. The following code demonstrates the code required to set up authentication with a mock provider.
+
+### Define a `SessionContext` to act as the mock authentication provider
+
+```tsx title="src/SessionContext.ts"
+import * as React from 'react';
+import type { Session } from '@toolpad/core';
+
+export interface SessionContextValue {
+ session: Session | null;
+ setSession: (session: Session | null) => void;
+}
+
+export const SessionContext = React.createContext({
+ session: {},
+ setSession: () => {},
+});
+
+export function useSession() {
+ return React.useContext(SessionContext);
+}
+```
+
+### Add the mock authentication and session data to the `AppProvider`
+
+```tsx title="src/App.tsx"
+import * as React from 'react';
+import DashboardIcon from '@mui/icons-material/Dashboard';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
+import { AppProvider } from '@toolpad/core/react-router-dom';
+import { Outlet, useNavigate } from 'react-router-dom';
+import type { Navigation, Session } from '@toolpad/core';
+import { SessionContext } from './SessionContext';
+
+const NAVIGATION: Navigation = [
+ {
+ kind: 'header',
+ title: 'Main items',
+ },
+ {
+ title: 'Dashboard',
+ icon: ,
+ },
+ {
+ segment: 'orders',
+ title: 'Orders',
+ icon: ,
+ },
+];
+
+const BRANDING = {
+ title: 'My Toolpad Core App',
+};
+
+export default function App() {
+ const [session, setSession] = React.useState(null);
+ const navigate = useNavigate();
+
+ const signIn = React.useCallback(() => {
+ navigate('/sign-in');
+ }, [navigate]);
+
+ const signOut = React.useCallback(() => {
+ setSession(null);
+ navigate('/sign-in');
+ }, [navigate]);
+
+ const sessionContextValue = React.useMemo(
+ () => ({ session, setSession }),
+ [session, setSession],
+ );
+
+ return (
+
+
+
+
+
+ );
+}
+```
+
+### Protect routes inside the dashboard layout
+
+```tsx title="src/layouts/dashboard.tsx"
+import * as React from 'react';
+import { Outlet, Navigate, useLocation } from 'react-router-dom';
+import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+import { PageContainer } from '@toolpad/core/PageContainer';
+import { useSession } from '../SessionContext';
+
+export default function Layout() {
+ const { session } = useSession();
+ const location = useLocation();
+
+ if (!session) {
+ // Add the `callbackUrl` search parameter
+ const redirectTo = `/sign-in?callbackUrl=${encodeURIComponent(location.pathname)}`;
+
+ return ;
+ }
+
+ return (
+
+
+
+
+
+ );
+}
+```
+
+You can protect any page or groups of pages through this mechanism.
+
+### Use the `SignInPage` component to create a sign-in page
+
+```tsx title="src/pages/signIn.tsx"
+'use client';
+import * as React from 'react';
+import { SignInPage } from '@toolpad/core/SignInPage';
+import type { Session } from '@toolpad/core/AppProvider';
+import { useNavigate } from 'react-router-dom';
+import { useSession } from '../SessionContext';
+
+const fakeAsyncGetSession = async (formData: any): Promise => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ if (formData.get('password') === 'password') {
+ resolve({
+ user: {
+ name: 'Bharat Kashyap',
+ email: formData.get('email') || '',
+ image: 'https://avatars.githubusercontent.com/u/19550456',
+ },
+ });
+ }
+ reject(new Error('Incorrect credentials.'));
+ }, 1000);
+ });
+};
+
+export default function SignIn() {
+ const { setSession } = useSession();
+ const navigate = useNavigate();
+ return (
+ {
+ // Demo session
+ try {
+ const session = await fakeAsyncGetSession(formData);
+ if (session) {
+ setSession(session);
+ navigate(callbackUrl || '/', { replace: true });
+ return {};
+ }
+ } catch (error) {
+ return {
+ error: error instanceof Error ? error.message : 'An error occurred',
+ };
+ }
+ return {};
+ }}
+ />
+ );
+}
+```
+
+### Add the sign in page to the router
+
+```tsx title="src/main.tsx"
+import * as React from 'react';
+import * as ReactDOM from 'react-dom/client';
+import { createBrowserRouter, RouterProvider } from 'react-router-dom';
+import App from './App';
+import Layout from './layouts/dashboard';
+import DashboardPage from './pages';
+import OrdersPage from './pages/orders';
+import SignInPage from './pages/signIn';
+
+const router = createBrowserRouter([
+ {
+ Component: App,
+ children: [
+ {
+ path: '/',
+ Component: Layout,
+ children: [
+ {
+ path: '/',
+ Component: DashboardPage,
+ },
+ {
+ path: '/orders',
+ Component: OrdersPage,
+ },
+ ],
+ },
+ {
+ path: '/sign-in',
+ Component: SignInPage,
+ },
+ ],
+ },
+]);
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+);
+```
+
+:::info
+For a full working example, see the [Toolpad Core Vite app with React Router and authentication example](https://github.com/mui/toolpad/tree/master/examples/core/auth-vite/)
+:::
diff --git a/docs/data/toolpad/core/introduction/integration.md b/docs/data/toolpad/core/introduction/integration.md
deleted file mode 100644
index 01569a2b7dd..00000000000
--- a/docs/data/toolpad/core/introduction/integration.md
+++ /dev/null
@@ -1,1062 +0,0 @@
----
-title: Toolpad Core - Integration
----
-
-# Integration
-
-This guide walks you through adding Toolpad Core to an existing project.
-
-## Installation
-
-
-
-```bash npm
-npm install @toolpad/core
-```
-
-```bash pnpm
-pnpm add @toolpad/core
-```
-
-```bash yarn
-yarn add @toolpad/core
-```
-
-
-
-## Next.js App Router
-
-Use the following steps to integrate Toolpad Core into your Next.js app:
-
-### 1. Wrap your application with `AppProvider`
-
-In your root layout file (for example, `app/layout.tsx`), wrap your application with the `AppProvider`:
-
-```tsx title="app/layout.tsx"
-import { AppProvider } from '@toolpad/core/AppProvider';
-import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter';
-
-export default function RootLayout({ children }: { children: React.ReactNode }) {
- return (
-
-
- {children}
-
-
- );
-}
-```
-
-You can find details on the `AppProvider` props on the [AppProvider](/toolpad/core/react-app-provider/) page.
-
-:::info
-The `AppRouterCacheProvider` component is not required to use Toolpad Core, but it's recommended to use it to ensure that the styles are appended to the `` and not rendering in the ``.
-
-See the [MaterialĀ UI Next.js integration docs](https://mui.com/material-ui/integrations/nextjs/) for more details.
-:::
-
-### 2. Create a dashboard layout
-
-Create a layout file for your dashboard pages (for example, `app/(dashboard)/layout.tsx`):
-
-```tsx title="app/(dashboard)/layout.tsx"
-import * as React from 'react';
-import { DashboardLayout } from '@toolpad/core/DashboardLayout';
-import { PageContainer } from '@toolpad/core/PageContainer';
-
-export default function DashboardPagesLayout(props: { children: React.ReactNode }) {
- return (
-
- {props.children}
-
- );
-}
-```
-
-The [`DashboardLayout`](/toolpad/core/react-dashboard-layout/) component provides a consistent layout for your dashboard pages, including a sidebar, navigation, and header. The [`PageContainer`](/toolpad/core/react-page-container/) component is used to wrap the page content, and provides breadcrumbs for navigation.
-
-### 3. Create a dashboard page
-
-Now you can create pages within your dashboard. For example, a home page (`app/(dashboard)/page.tsx`):
-
-```tsx title="app/(dashboard)/page.tsx"
-import * as React from 'react';
-import Typography from '@mui/material/Typography';
-
-export default function Page() {
- return Welcome to a page in the dashboard!;
-}
-```
-
-That's it! You have now integrated Toolpad Core into your Next.js app.
-
-### 4. (Optional) Add a second page
-
-Create a new page in the dashboard, for example, `app/(dashboard)/orders/page.tsx`:
-
-```tsx title="app/(dashboard)/orders/page.tsx"
-import * as React from 'react';
-import Typography from '@mui/material/Typography';
-
-export default function OrdersPage() {
- return Welcome to the orders page!;
-}
-```
-
-To add this page to the navigation, add it to the `NAVIGATION` variable:
-
-```ts title="app/layout.tsx"
-export const NAVIGATION = [
- // ...
- {
- segment: 'orders',
- title: 'Orders',
- icon: ,
- },
- // ...
-];
-```
-
-### 5. (Optional) Set up authentication
-
-If you want to add authentication, you can use Auth.js with Toolpad Core. Here's an example setup:
-
-#### a. Install the dependencies
-
-```bash
-npm install next-auth@beta
-```
-
-#### b. Create an `auth.ts` file
-
-```ts title="auth.ts"
-import NextAuth from 'next-auth';
-import GitHub from 'next-auth/providers/github';
-import type { Provider } from 'next-auth/providers';
-
-const providers: Provider[] = [
- GitHub({
- clientId: process.env.GITHUB_CLIENT_ID,
- clientSecret: process.env.GITHUB_CLIENT_SECRET,
- }),
-];
-
-export const providerMap = providers.map((provider) => {
- if (typeof provider === 'function') {
- const providerData = provider();
- return { id: providerData.id, name: providerData.name };
- }
- return { id: provider.id, name: provider.name };
-});
-
-export const { handlers, auth, signIn, signOut } = NextAuth({
- providers,
- secret: process.env.AUTH_SECRET,
- pages: {
- signIn: '/auth/signin',
- },
- callbacks: {
- authorized({ auth: session, request: { nextUrl } }) {
- const isLoggedIn = !!session?.user;
- const isPublicPage = nextUrl.pathname.startsWith('/public');
-
- if (isPublicPage || isLoggedIn) {
- return true;
- }
-
- return false; // Redirect unauthenticated users to login page
- },
- },
-});
-```
-
-#### c. Create a sign-in page
-
-Use the `SignInPage` component to add a sign-in page to your app. For example, `app/auth/signin/page.tsx`:
-
-```tsx title="app/auth/signin/page.tsx"
-import * as React from 'react';
-import { SignInPage, type AuthProvider } from '@toolpad/core/SignInPage';
-import { AuthError } from 'next-auth';
-import { providerMap, signIn } from '../../../auth';
-
-export default function SignIn() {
- return (
- {
- 'use server';
- try {
- return await signIn(provider.id, {
- redirectTo: callbackUrl ?? '/',
- });
- } catch (error) {
- // The desired flow for successful sign in in all cases
- // and unsuccessful sign in for OAuth providers will cause a `redirect`,
- // and `redirect` is a throwing function, so we need to re-throw
- // to allow the redirect to happen
- // Source: https://github.com/vercel/next.js/issues/49298#issuecomment-1542055642
- // Detect a `NEXT_REDIRECT` error and re-throw it
- if (error instanceof Error && error.message === 'NEXT_REDIRECT') {
- throw error;
- }
- // Handle Auth.js errors
- if (error instanceof AuthError) {
- return {
- error: error.message,
- type: error.type,
- };
- }
- // An error boundary must exist to handle unknown errors
- return {
- error: 'Something went wrong.',
- type: 'UnknownError',
- };
- }
- }}
- />
- );
-}
-```
-
-#### d. Create a route handler for sign-in
-
-`next-auth` requires a route handler for sign-in. Create a file `app/api/auth/[...nextauth]/route.ts`:
-
-```ts title="app/api/auth/[...nextauth]/route.ts"
-import { handlers } from '../../../../auth';
-
-export const { GET, POST } = handlers;
-```
-
-#### e. Add a middleware
-
-Add a middleware to your app to protect your dashboard pages:
-
-```ts title="middleware.ts"
-export { auth as middleware } from './auth';
-
-export const config = {
- // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
- matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
-};
-```
-
-That's it! You now have Toolpad Core integrated into your Next.js App Router app with authentication setup:
-
-{{"component": "modules/components/DocsImage.tsx", "src": "/static/toolpad/docs/core/integration-nextjs-app.png", "srcDark": "/static/toolpad/docs/core/integration-nextjs-app-dark.png", "alt": "Next.js App Router with Toolpad Core", "caption": "Next.js App Router with Toolpad Core", "zoom": true, "aspectRatio": "1.428" }}
-
-:::info
-For a full working example with authentication included, see the [Toolpad Core Next.js App with Auth.js example](https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs/)
-:::
-
-## Next.js Pages Router
-
-To integrate Toolpad Core into your Next.js Pages Router app, follow these steps:
-
-### 1. Wrap your application with `AppProvider`
-
-In your root layout file (for example, `pages/_app.tsx`), wrap your application with the `AppProvider`:
-
-```tsx title="pages/_app.tsx"
-import * as React from 'react';
-import { AppProvider } from '@toolpad/core/nextjs';
-import { PageContainer } from '@toolpad/core/PageContainer';
-import { DashboardLayout } from '@toolpad/core/DashboardLayout';
-import Head from 'next/head';
-import { AppCacheProvider } from '@mui/material-nextjs/v14-pagesRouter';
-import DashboardIcon from '@mui/icons-material/Dashboard';
-import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
-import type { Navigation } from '@toolpad/core/AppProvider';
-
-const NAVIGATION: Navigation = [
- {
- kind: 'header',
- title: 'Main items',
- },
- {
- segment: '',
- title: 'Dashboard',
- icon: ,
- },
-];
-
-const BRANDING = {
- title: 'My Toolpad Core App',
-};
-
-export default function App({ Component }: { Component: React.ElementType }) {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-```
-
-:::info
-The `AppCacheProvider` component is not required to use Toolpad Core, but it's recommended.
-
-See the [Material UI Next.js Pages Router integration docs](https://mui.com/material-ui/integrations/nextjs/#configuration-2) for more details.
-:::
-
-### 2. Modify `_document.tsx`
-
-Modify `_document.tsx` to include the `DocumentHeadTags` component:
-
-```tsx title="pages/_document.tsx"
-import * as React from 'react';
-import {
- Html,
- Head,
- Main,
- NextScript,
- DocumentProps,
- DocumentContext,
-} from 'next/document';
-import {
- DocumentHeadTags,
- DocumentHeadTagsProps,
- documentGetInitialProps,
-} from '@mui/material-nextjs/v14-pagesRouter';
-
-export default function Document(props: DocumentProps & DocumentHeadTagsProps) {
- return (
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-Document.getInitialProps = async (ctx: DocumentContext) => {
- const finalProps = await documentGetInitialProps(ctx);
- return finalProps;
-};
-```
-
-### 3. Add a dashboard page
-
-Create a dashboard page (for example, `pages/index.tsx`):
-
-```tsx title="pages/index.tsx"
-import * as React from 'react';
-import Typography from '@mui/material/Typography';
-
-export default function HomePage() {
- return Welcome to Toolpad!;
-}
-```
-
-### 4. (Optional) Add a second page
-
-Create a new page in the dashboard, for example, `pages/orders/index.tsx`:
-
-```tsx title="pages/orders/index.tsx"
-import * as React from 'react';
-import Typography from '@mui/material/Typography';
-
-export default function OrdersPage() {
- return Welcome to the orders page!;
-}
-```
-
-To add this page to the navigation, add it to the `NAVIGATION` variable:
-
-```ts title="pages/_app.tsx"
-export const NAVIGATION = [
- // ...
- {
- segment: 'orders',
- title: 'Orders',
- icon: ,
- },
- // ...
-];
-```
-
-### 5. (Optional) Set up authentication
-
-If you want to add authentication, you can use Auth.js with Toolpad Core. Here's an example setup:
-
-#### a. Install the dependencies
-
-```bash
-npm install next-auth@beta
-```
-
-#### b. Create an `auth.ts` file
-
-```ts title="auth.ts"
-import NextAuth from 'next-auth';
-import GitHub from 'next-auth/providers/github';
-import type { Provider } from 'next-auth/providers';
-
-const providers: Provider[] = [
- GitHub({
- clientId: process.env.GITHUB_CLIENT_ID,
- clientSecret: process.env.GITHUB_CLIENT_SECRET,
- }),
-];
-
-export const providerMap = providers.map((provider) => {
- if (typeof provider === 'function') {
- const providerData = provider();
- return { id: providerData.id, name: providerData.name };
- }
- return { id: provider.id, name: provider.name };
-});
-
-export const { handlers, auth } = NextAuth({
- providers,
- secret: process.env.AUTH_SECRET,
- pages: {
- signIn: '/auth/signin',
- },
- callbacks: {
- authorized({ auth: session, request: { nextUrl } }) {
- const isLoggedIn = !!session?.user;
- const isPublicPage = nextUrl.pathname.startsWith('/public');
-
- if (isPublicPage || isLoggedIn) {
- return true;
- }
-
- return false; // Redirect unauthenticated users to login page
- },
- },
-});
-```
-
-#### c. Modify `_app.tsx`
-
-Modify `_app.tsx` to include the `authentication` prop and other helpers:
-
-```tsx title="pages/_app.tsx"
-import * as React from 'react';
-import { AppProvider } from '@toolpad/core/nextjs';
-import { DashboardLayout } from '@toolpad/core/DashboardLayout';
-import { PageContainer } from '@toolpad/core/PageContainer';
-import Head from 'next/head';
-import { AppCacheProvider } from '@mui/material-nextjs/v14-pagesRouter';
-import DashboardIcon from '@mui/icons-material/Dashboard';
-import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
-import type { NextPage } from 'next';
-import type { AppProps } from 'next/app';
-import type { Navigation } from '@toolpad/core/AppProvider';
-import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react';
-import LinearProgress from '@mui/material/LinearProgress';
-
-export type NextPageWithLayout = NextPage
& {
- getLayout?: (page: React.ReactElement) => React.ReactNode;
- requireAuth?: boolean;
-};
-
-type AppPropsWithLayout = AppProps & {
- Component: NextPageWithLayout;
-};
-
-const NAVIGATION: Navigation = [
- {
- kind: 'header',
- title: 'Main items',
- },
- {
- segment: '',
- title: 'Dashboard',
- icon: ,
- },
- {
- segment: 'orders',
- title: 'Orders',
- icon: ,
- },
-];
-
-const BRANDING = {
- title: 'My Toolpad Core App',
-};
-
-const AUTHENTICATION = {
- signIn,
- signOut,
-};
-
-function getDefaultLayout(page: React.ReactElement) {
- return (
-
- {page}
-
- );
-}
-
-function RequireAuth({ children }: { children: React.ReactNode }) {
- const { status } = useSession();
-
- if (status === 'loading') {
- return ;
- }
-
- return children;
-}
-
-function AppLayout({ children }: { children: React.ReactNode }) {
- const { data: session } = useSession();
- return (
-
-
-
-
-
- {children}
-
-
- );
-}
-
-export default function App(props: AppPropsWithLayout) {
- const {
- Component,
- pageProps: { session, ...pageProps },
- } = props;
-
- const getLayout = Component.getLayout ?? getDefaultLayout;
- const requireAuth = Component.requireAuth ?? true;
-
- let pageContent = getLayout();
- if (requireAuth) {
- pageContent = {pageContent};
- }
- pageContent = {pageContent};
-
- return (
-
- {pageContent}
-
- );
-}
-```
-
-#### d. Create a sign-in page
-
-Use the `SignInPage` component to add a sign-in page to your app. For example, `pages/auth/signin.tsx`:
-
-```tsx title="pages/auth/signin.tsx"
-import * as React from 'react';
-import type { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
-import Link from '@mui/material/Link';
-import { SignInPage } from '@toolpad/core/SignInPage';
-import { signIn } from 'next-auth/react';
-import { useRouter } from 'next/router';
-import { auth, providerMap } from '../../auth';
-
-export default function SignIn({
- providers,
-}: InferGetServerSidePropsType) {
- const router = useRouter();
- return (
- {
- try {
- const signInResponse = await signIn(provider.id, {
- callbackUrl: callbackUrl ?? '/',
- });
- if (signInResponse && signInResponse.error) {
- // Handle Auth.js errors
- return {
- error: signInResponse.error.message,
- type: signInResponse.error,
- };
- }
- return {};
- } catch (error) {
- // An error boundary must exist to handle unknown errors
- return {
- error: 'Something went wrong.',
- type: 'UnknownError',
- };
- }
- }}
- />
- );
-}
-
-SignIn.getLayout = (page: React.ReactNode) => page;
-
-SignIn.requireAuth = false;
-
-export async function getServerSideProps(context: GetServerSidePropsContext) {
- const session = await auth(context);
-
- // If the user is already logged in, redirect.
- // Note: Make sure not to redirect to the same page
- // To avoid an infinite loop!
- if (session) {
- return { redirect: { destination: '/' } };
- }
-
- return {
- props: {
- providers: providerMap,
- },
- };
-}
-```
-
-#### e. Create a route handler for sign-in
-
-`next-auth` requires a route handler for sign-in. Create a file `app/api/auth/[...nextauth].ts`:
-
-```ts title="app/api/auth/[...nextauth].ts"
-import { handlers } from '../../../../auth';
-
-export const { GET, POST } = handlers;
-```
-
-:::warning
-
-Note that this file is a route handler and must be placed in the `app` directory, even if the rest of your app is in the `pages` directory. Know more in the [Auth.js documentation](https://authjs.dev/getting-started/installation#configure).
-
-:::
-
-#### f. Add a middleware
-
-Add a middleware to your app to protect your dashboard pages:
-
-```ts title="middleware.ts"
-export { auth as middleware } from './auth';
-
-export const config = {
- // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
- matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
-};
-```
-
-That's it! You now have Toolpad Core integrated into your Next.js Pages Router app with authentication setup:
-
-{{"component": "modules/components/DocsImage.tsx", "src": "/static/toolpad/docs/core/integration-nextjs-pages.png", "srcDark": "/static/toolpad/docs/core/integration-nextjs-pages-dark.png", "alt": "Next.js Pages Router with Toolpad Core", "caption": "Next.js Pages Router with Toolpad Core", "zoom": true, "aspectRatio": "1.428" }}
-
-:::info
-For a full working example with authentication included, see the [Toolpad Core Next.js Pages app with Auth.js example](https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs-pages/)
-:::
-
-## React Router
-
-To integrate Toolpad Core into a single-page app (with [Vite](https://vite.dev/), for example) using **React Router**, follow these steps:
-
-### 1. Wrap all your pages in an `AppProvider`
-
-In your router configuration (e.g.: `src/main.tsx`), use a shared component or element (e.g.: `src/App.tsx`) as a root **layout route** that will wrap the whole application with the `AppProvider` from `@toolpad/core/react-router-dom`.
-
-You must use the `` component from `react-router-dom` in this root layout element or component.
-
-```tsx title="src/main.tsx"
-import * as React from 'react';
-import * as ReactDOM from 'react-dom/client';
-import { createBrowserRouter, RouterProvider } from 'react-router-dom';
-import App from './App';
-import DashboardPage from './pages';
-import OrdersPage from './pages/orders';
-
-const router = createBrowserRouter([
- {
- Component: App, // root layout route
- },
-]);
-
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
- ,
-);
-```
-
-```tsx title="src/App.tsx"
-import * as React from 'react';
-import DashboardIcon from '@mui/icons-material/Dashboard';
-import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
-import { AppProvider } from '@toolpad/core/react-router-dom';
-import { Outlet } from 'react-router-dom';
-import type { Navigation } from '@toolpad/core';
-
-const NAVIGATION: Navigation = [
- {
- kind: 'header',
- title: 'Main items',
- },
- {
- title: 'Dashboard',
- icon: ,
- },
- {
- segment: 'orders',
- title: 'Orders',
- icon: ,
- },
-];
-
-const BRANDING = {
- title: 'My Toolpad Core App',
-};
-
-export default function App() {
- return (
-
-
-
- );
-}
-```
-
-### 2. Create a dashboard layout
-
-Create a layout file for your dashboard pages (e.g.: `src/layouts/dashboard.tsx`), to also be used as a layout route with the `` component from `react-router-dom`:
-
-```tsx title="src/layouts/dashboard.tsx"
-import * as React from 'react';
-import { Outlet } from 'react-router-dom';
-import { DashboardLayout } from '@toolpad/core/DashboardLayout';
-import { PageContainer } from '@toolpad/core/PageContainer';
-
-export default function Layout() {
- return (
-
-
-
-
-
- );
-}
-```
-
-The [`DashboardLayout`](/toolpad/core/react-dashboard-layout/) component provides a consistent layout for your dashboard pages, including a sidebar, navigation, and header. The [`PageContainer`](/toolpad/core/react-page-container/) component is used to wrap the page content, and provides breadcrumbs for navigation.
-
-You can then add this layout component to your React Router configuration (e.g.: `src/main.tsx`), as a child of the root layout route created in step 1.
-
-```tsx title="src/main.tsx"
-import Layout from './layouts/dashboard';
-
-//...
-const router = createBrowserRouter([
- {
- Component: App, // root layout route
- children: [
- {
- path: '/',
- Component: Layout,
- },
- ],
- },
-]);
-//...
-```
-
-### 3. Create pages
-
-Create a dashboard page (e.g.: `src/pages/index.tsx`) and an orders page (`src/pages/orders.tsx`).
-
-```tsx title="src/pages/index.tsx"
-import * as React from 'react';
-import Typography from '@mui/material/Typography';
-
-export default function DashboardPage() {
- return Welcome to Toolpad!;
-}
-```
-
-```tsx title="src/pages/orders.tsx"
-import * as React from 'react';
-import Typography from '@mui/material/Typography';
-
-export default function OrdersPage() {
- return Welcome to the Toolpad orders!;
-}
-```
-
-You can then add these page components as routes to your React Router configuration (e.g.: `src/main.tsx`). By adding them as children of the layout route created in step 2, they will automatically be wrapped with that dashboard layout:
-
-```tsx title="src/main.tsx"
-import DashboardPage from './pages';
-import OrdersPage from './pages/orders';
-
-//...
-const router = createBrowserRouter([
- {
- Component: App, // root layout route
- children: [
- {
- path: '/',
- Component: Layout,
- children: [
- {
- path: '',
- Component: DashboardPage,
- },
- {
- path: 'orders',
- Component: OrdersPage,
- },
- ],
- },
- ],
- },
-]);
-//...
-```
-
-That's it! You now have Toolpad Core integrated into your single-page app with React Router!
-
-{{"demo": "ReactRouter.js", "height": 500, "iframe": true, "hideToolbar": true}}
-
-:::info
-For a full working example, see the [Toolpad Core Vite app with React Router example](https://github.com/mui/toolpad/tree/master/examples/core/vite/)
-:::
-
-### 4. (Optional) Set up authentication
-
-You can use the `SignInPage` component to add authentication along with an external authentication provider of your choice. The following code demonstrates the code required to set up authentication with a mock provider.
-
-#### a. Define a `SessionContext` to act as the mock authentication provider
-
-```tsx title="src/SessionContext.ts"
-import * as React from 'react';
-import type { Session } from '@toolpad/core';
-
-export interface SessionContextValue {
- session: Session | null;
- setSession: (session: Session | null) => void;
-}
-
-export const SessionContext = React.createContext({
- session: {},
- setSession: () => {},
-});
-
-export function useSession() {
- return React.useContext(SessionContext);
-}
-```
-
-### b. Add the mock authentication and session data to the `AppProvider`
-
-```tsx title="src/App.tsx"
-import * as React from 'react';
-import DashboardIcon from '@mui/icons-material/Dashboard';
-import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
-import { AppProvider } from '@toolpad/core/react-router-dom';
-import { Outlet, useNavigate } from 'react-router-dom';
-import type { Navigation, Session } from '@toolpad/core';
-import { SessionContext } from './SessionContext';
-
-const NAVIGATION: Navigation = [
- {
- kind: 'header',
- title: 'Main items',
- },
- {
- title: 'Dashboard',
- icon: ,
- },
- {
- segment: 'orders',
- title: 'Orders',
- icon: ,
- },
-];
-
-const BRANDING = {
- title: 'My Toolpad Core App',
-};
-
-export default function App() {
- const [session, setSession] = React.useState(null);
- const navigate = useNavigate();
-
- const signIn = React.useCallback(() => {
- navigate('/sign-in');
- }, [navigate]);
-
- const signOut = React.useCallback(() => {
- setSession(null);
- navigate('/sign-in');
- }, [navigate]);
-
- const sessionContextValue = React.useMemo(
- () => ({ session, setSession }),
- [session, setSession],
- );
-
- return (
-
-
-
-
-
- );
-}
-```
-
-#### c. Protect routes inside the dashboard layout
-
-```tsx title="src/layouts/dashboard.tsx"
-import * as React from 'react';
-import { Outlet, Navigate, useLocation } from 'react-router-dom';
-import { DashboardLayout } from '@toolpad/core/DashboardLayout';
-import { PageContainer } from '@toolpad/core/PageContainer';
-import { useSession } from '../SessionContext';
-
-export default function Layout() {
- const { session } = useSession();
- const location = useLocation();
-
- if (!session) {
- // Add the `callbackUrl` search parameter
- const redirectTo = `/sign-in?callbackUrl=${encodeURIComponent(location.pathname)}`;
-
- return ;
- }
-
- return (
-
-
-
-
-
- );
-}
-```
-
-You can protect any page or groups of pages through this mechanism.
-
-#### d. Use the `SignInPage` component to create a sign-in page
-
-```tsx title="src/pages/signIn.tsx"
-'use client';
-import * as React from 'react';
-import { SignInPage } from '@toolpad/core/SignInPage';
-import type { Session } from '@toolpad/core/AppProvider';
-import { useNavigate } from 'react-router-dom';
-import { useSession } from '../SessionContext';
-
-const fakeAsyncGetSession = async (formData: any): Promise => {
- return new Promise((resolve, reject) => {
- setTimeout(() => {
- if (formData.get('password') === 'password') {
- resolve({
- user: {
- name: 'Bharat Kashyap',
- email: formData.get('email') || '',
- image: 'https://avatars.githubusercontent.com/u/19550456',
- },
- });
- }
- reject(new Error('Incorrect credentials.'));
- }, 1000);
- });
-};
-
-export default function SignIn() {
- const { setSession } = useSession();
- const navigate = useNavigate();
- return (
- {
- // Demo session
- try {
- const session = await fakeAsyncGetSession(formData);
- if (session) {
- setSession(session);
- navigate(callbackUrl || '/', { replace: true });
- return {};
- }
- } catch (error) {
- return {
- error: error instanceof Error ? error.message : 'An error occurred',
- };
- }
- return {};
- }}
- />
- );
-}
-```
-
-#### e. Add the sign in page to the router
-
-```tsx title="src/main.tsx"
-import * as React from 'react';
-import * as ReactDOM from 'react-dom/client';
-import { createBrowserRouter, RouterProvider } from 'react-router-dom';
-import App from './App';
-import Layout from './layouts/dashboard';
-import DashboardPage from './pages';
-import OrdersPage from './pages/orders';
-import SignInPage from './pages/signIn';
-
-const router = createBrowserRouter([
- {
- Component: App,
- children: [
- {
- path: '/',
- Component: Layout,
- children: [
- {
- path: '/',
- Component: DashboardPage,
- },
- {
- path: '/orders',
- Component: OrdersPage,
- },
- ],
- },
- {
- path: '/sign-in',
- Component: SignInPage,
- },
- ],
- },
-]);
-
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
- ,
-);
-```
-
-:::info
-For a full working example, see the [Toolpad Core Vite app with React Router and authentication example](https://github.com/mui/toolpad/tree/master/examples/core/auth-vite/)
-:::
diff --git a/docs/data/toolpad/core/pages.ts b/docs/data/toolpad/core/pages.ts
index 090e958a2a1..7846c425724 100644
--- a/docs/data/toolpad/core/pages.ts
+++ b/docs/data/toolpad/core/pages.ts
@@ -20,10 +20,6 @@ const pages: MuiPage[] = [
pathname: '/toolpad/core/introduction/base-concepts',
title: 'Base concepts',
},
- {
- pathname: '/toolpad/core/introduction/integration',
- title: 'Integration',
- },
{
pathname: '/toolpad/core/introduction/tutorial',
title: 'Tutorial',
@@ -42,6 +38,24 @@ const pages: MuiPage[] = [
},
],
},
+ {
+ pathname: '/toolpad/core/integrations-group',
+ title: 'Integrations',
+ children: [
+ {
+ pathname: '/toolpad/core/integrations/nextjs-approuter',
+ title: 'Next.js App Router',
+ },
+ {
+ pathname: '/toolpad/core/integrations/nextjs-pagesrouter',
+ title: 'Next.js Page Router',
+ },
+ {
+ pathname: '/toolpad/core/integrations/react-router',
+ title: 'React Router',
+ },
+ ],
+ },
{
pathname: '/toolpad/core/components-group',
title: 'Components',
diff --git a/docs/pages/toolpad/core/integrations/nextjs-approuter.js b/docs/pages/toolpad/core/integrations/nextjs-approuter.js
new file mode 100644
index 00000000000..e8bbf70089c
--- /dev/null
+++ b/docs/pages/toolpad/core/integrations/nextjs-approuter.js
@@ -0,0 +1,7 @@
+import * as React from 'react';
+import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
+import * as pageProps from '../../../../data/toolpad/core/integrations/nextjs-approuter.md?muiMarkdown';
+
+export default function Page() {
+ return ;
+}
diff --git a/docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js b/docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
new file mode 100644
index 00000000000..8be2843cb83
--- /dev/null
+++ b/docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
@@ -0,0 +1,7 @@
+import * as React from 'react';
+import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
+import * as pageProps from '../../../../data/toolpad/core/integrations/nextjs-pagesrouter.md?muiMarkdown';
+
+export default function Page() {
+ return ;
+}
diff --git a/docs/pages/toolpad/core/introduction/integration.js b/docs/pages/toolpad/core/integrations/react-router.js
similarity index 65%
rename from docs/pages/toolpad/core/introduction/integration.js
rename to docs/pages/toolpad/core/integrations/react-router.js
index 2e29678e8da..b34ba5230fb 100644
--- a/docs/pages/toolpad/core/introduction/integration.js
+++ b/docs/pages/toolpad/core/integrations/react-router.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
-import * as pageProps from '../../../../data/toolpad/core/introduction/integration.md?muiMarkdown';
+import * as pageProps from '../../../../data/toolpad/core/integrations/react-router.md?muiMarkdown';
export default function Page() {
return ;
From 60f6b1cde0fe53e5e78fc6fa48e0732b79c4563b Mon Sep 17 00:00:00 2001
From: Prakhar Gupta <92228082+prakhargupta1@users.noreply.github.com>
Date: Wed, 13 Nov 2024 14:23:19 +0300
Subject: [PATCH 2/6] pages
---
docs/data/toolpad/core/pages.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/data/toolpad/core/pages.ts b/docs/data/toolpad/core/pages.ts
index 7846c425724..e41be818fd5 100644
--- a/docs/data/toolpad/core/pages.ts
+++ b/docs/data/toolpad/core/pages.ts
@@ -48,7 +48,7 @@ const pages: MuiPage[] = [
},
{
pathname: '/toolpad/core/integrations/nextjs-pagesrouter',
- title: 'Next.js Page Router',
+ title: 'Next.js Pages Router',
},
{
pathname: '/toolpad/core/integrations/react-router',
From f590402662c1c5fd492416569c4571a1f11ec4ed Mon Sep 17 00:00:00 2001
From: Prakhar Gupta <92228082+prakhargupta1@users.noreply.github.com>
Date: Thu, 14 Nov 2024 10:02:22 +0300
Subject: [PATCH 3/6] better-pathname
---
.../core/integrations/{nextjs-approuter.md => app-router.md} | 0
.../integrations/{nextjs-pagesrouter.md => pages-router.md} | 0
docs/data/toolpad/core/pages.ts | 4 ++--
.../core/integrations/{nextjs-approuter.js => app-router.js} | 2 +-
.../integrations/{nextjs-pagesrouter.js => pages-router.js} | 2 +-
5 files changed, 4 insertions(+), 4 deletions(-)
rename docs/data/toolpad/core/integrations/{nextjs-approuter.md => app-router.md} (100%)
rename docs/data/toolpad/core/integrations/{nextjs-pagesrouter.md => pages-router.md} (100%)
rename docs/pages/toolpad/core/integrations/{nextjs-approuter.js => app-router.js} (86%)
rename docs/pages/toolpad/core/integrations/{nextjs-pagesrouter.js => pages-router.js} (85%)
diff --git a/docs/data/toolpad/core/integrations/nextjs-approuter.md b/docs/data/toolpad/core/integrations/app-router.md
similarity index 100%
rename from docs/data/toolpad/core/integrations/nextjs-approuter.md
rename to docs/data/toolpad/core/integrations/app-router.md
diff --git a/docs/data/toolpad/core/integrations/nextjs-pagesrouter.md b/docs/data/toolpad/core/integrations/pages-router.md
similarity index 100%
rename from docs/data/toolpad/core/integrations/nextjs-pagesrouter.md
rename to docs/data/toolpad/core/integrations/pages-router.md
diff --git a/docs/data/toolpad/core/pages.ts b/docs/data/toolpad/core/pages.ts
index e41be818fd5..3a28f0bc40e 100644
--- a/docs/data/toolpad/core/pages.ts
+++ b/docs/data/toolpad/core/pages.ts
@@ -43,11 +43,11 @@ const pages: MuiPage[] = [
title: 'Integrations',
children: [
{
- pathname: '/toolpad/core/integrations/nextjs-approuter',
+ pathname: '/toolpad/core/integrations/app-router',
title: 'Next.js App Router',
},
{
- pathname: '/toolpad/core/integrations/nextjs-pagesrouter',
+ pathname: '/toolpad/core/integrations/pages-router',
title: 'Next.js Pages Router',
},
{
diff --git a/docs/pages/toolpad/core/integrations/nextjs-approuter.js b/docs/pages/toolpad/core/integrations/app-router.js
similarity index 86%
rename from docs/pages/toolpad/core/integrations/nextjs-approuter.js
rename to docs/pages/toolpad/core/integrations/app-router.js
index e8bbf70089c..d8ec2556d29 100644
--- a/docs/pages/toolpad/core/integrations/nextjs-approuter.js
+++ b/docs/pages/toolpad/core/integrations/app-router.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
-import * as pageProps from '../../../../data/toolpad/core/integrations/nextjs-approuter.md?muiMarkdown';
+import * as pageProps from '../../../../data/toolpad/core/integrations/app-router.md?muiMarkdown';
export default function Page() {
return ;
diff --git a/docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js b/docs/pages/toolpad/core/integrations/pages-router.js
similarity index 85%
rename from docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
rename to docs/pages/toolpad/core/integrations/pages-router.js
index 8be2843cb83..36ea5d98768 100644
--- a/docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
+++ b/docs/pages/toolpad/core/integrations/pages-router.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
-import * as pageProps from '../../../../data/toolpad/core/integrations/nextjs-pagesrouter.md?muiMarkdown';
+import * as pageProps from '../../../../data/toolpad/core/integrations/pages-router.md?muiMarkdown';
export default function Page() {
return ;
From 65efddc27322daa92b263b03f9f07ed13f2b2f3b Mon Sep 17 00:00:00 2001
From: Prakhar Gupta <92228082+prakhargupta1@users.noreply.github.com>
Date: Fri, 15 Nov 2024 10:05:53 +0300
Subject: [PATCH 4/6] Vite
---
docs/data/toolpad/core/pages.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/data/toolpad/core/pages.ts b/docs/data/toolpad/core/pages.ts
index 3a28f0bc40e..c427a63d270 100644
--- a/docs/data/toolpad/core/pages.ts
+++ b/docs/data/toolpad/core/pages.ts
@@ -52,7 +52,7 @@ const pages: MuiPage[] = [
},
{
pathname: '/toolpad/core/integrations/react-router',
- title: 'React Router',
+ title: 'Vite with React Router',
},
],
},
From 0621f4083aef760ae888c79b0b157fc0c6228129 Mon Sep 17 00:00:00 2001
From: Prakhar Gupta <92228082+prakhargupta1@users.noreply.github.com>
Date: Fri, 22 Nov 2024 15:46:57 +0300
Subject: [PATCH 5/6] Revert "better-pathname"
This reverts commit f590402662c1c5fd492416569c4571a1f11ec4ed.
---
.../core/integrations/{app-router.md => nextjs-approuter.md} | 0
.../integrations/{pages-router.md => nextjs-pagesrouter.md} | 0
docs/data/toolpad/core/pages.ts | 4 ++--
.../core/integrations/{app-router.js => nextjs-approuter.js} | 2 +-
.../integrations/{pages-router.js => nextjs-pagesrouter.js} | 2 +-
5 files changed, 4 insertions(+), 4 deletions(-)
rename docs/data/toolpad/core/integrations/{app-router.md => nextjs-approuter.md} (100%)
rename docs/data/toolpad/core/integrations/{pages-router.md => nextjs-pagesrouter.md} (100%)
rename docs/pages/toolpad/core/integrations/{app-router.js => nextjs-approuter.js} (86%)
rename docs/pages/toolpad/core/integrations/{pages-router.js => nextjs-pagesrouter.js} (85%)
diff --git a/docs/data/toolpad/core/integrations/app-router.md b/docs/data/toolpad/core/integrations/nextjs-approuter.md
similarity index 100%
rename from docs/data/toolpad/core/integrations/app-router.md
rename to docs/data/toolpad/core/integrations/nextjs-approuter.md
diff --git a/docs/data/toolpad/core/integrations/pages-router.md b/docs/data/toolpad/core/integrations/nextjs-pagesrouter.md
similarity index 100%
rename from docs/data/toolpad/core/integrations/pages-router.md
rename to docs/data/toolpad/core/integrations/nextjs-pagesrouter.md
diff --git a/docs/data/toolpad/core/pages.ts b/docs/data/toolpad/core/pages.ts
index c427a63d270..b4c817776c9 100644
--- a/docs/data/toolpad/core/pages.ts
+++ b/docs/data/toolpad/core/pages.ts
@@ -43,11 +43,11 @@ const pages: MuiPage[] = [
title: 'Integrations',
children: [
{
- pathname: '/toolpad/core/integrations/app-router',
+ pathname: '/toolpad/core/integrations/nextjs-approuter',
title: 'Next.js App Router',
},
{
- pathname: '/toolpad/core/integrations/pages-router',
+ pathname: '/toolpad/core/integrations/nextjs-pagesrouter',
title: 'Next.js Pages Router',
},
{
diff --git a/docs/pages/toolpad/core/integrations/app-router.js b/docs/pages/toolpad/core/integrations/nextjs-approuter.js
similarity index 86%
rename from docs/pages/toolpad/core/integrations/app-router.js
rename to docs/pages/toolpad/core/integrations/nextjs-approuter.js
index d8ec2556d29..e8bbf70089c 100644
--- a/docs/pages/toolpad/core/integrations/app-router.js
+++ b/docs/pages/toolpad/core/integrations/nextjs-approuter.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
-import * as pageProps from '../../../../data/toolpad/core/integrations/app-router.md?muiMarkdown';
+import * as pageProps from '../../../../data/toolpad/core/integrations/nextjs-approuter.md?muiMarkdown';
export default function Page() {
return ;
diff --git a/docs/pages/toolpad/core/integrations/pages-router.js b/docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
similarity index 85%
rename from docs/pages/toolpad/core/integrations/pages-router.js
rename to docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
index 36ea5d98768..8be2843cb83 100644
--- a/docs/pages/toolpad/core/integrations/pages-router.js
+++ b/docs/pages/toolpad/core/integrations/nextjs-pagesrouter.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
-import * as pageProps from '../../../../data/toolpad/core/integrations/pages-router.md?muiMarkdown';
+import * as pageProps from '../../../../data/toolpad/core/integrations/nextjs-pagesrouter.md?muiMarkdown';
export default function Page() {
return ;
From 82f2b82f3b44c3eded37926f22ecfbe62821017d Mon Sep 17 00:00:00 2001
From: Prakhar Gupta <92228082+prakhargupta1@users.noreply.github.com>
Date: Fri, 22 Nov 2024 16:40:44 +0300
Subject: [PATCH 6/6] from review
---
docs/data/toolpad/core/integrations/react-router.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/data/toolpad/core/integrations/react-router.md b/docs/data/toolpad/core/integrations/react-router.md
index 1b0dde51476..e0fd48b0d82 100644
--- a/docs/data/toolpad/core/integrations/react-router.md
+++ b/docs/data/toolpad/core/integrations/react-router.md
@@ -8,7 +8,7 @@ title: React router - Integration
## Wrap all your pages in an `AppProvider`
-In your router configuration (e.g.: `src/main.tsx`), use a shared component or element (e.g.: `src/App.tsx`) as a root **layout route** that will wrap the whole application with the `AppProvider` from `@toolpad/core/react-router-dom`.
+In your router configuration (e.g.: `src/main.tsx`), use a shared component or element (e.g.: `src/App.tsx`) as a root **layout route** that wraps the whole application with the `AppProvider` from `@toolpad/core/react-router-dom`.
You must use the `` component from `react-router-dom` in this root layout element or component.
@@ -93,7 +93,7 @@ export default function Layout() {
The [`DashboardLayout`](/toolpad/core/react-dashboard-layout/) component provides a consistent layout for your dashboard pages, including a sidebar, navigation, and header. The [`PageContainer`](/toolpad/core/react-page-container/) component is used to wrap the page content, and provides breadcrumbs for navigation.
-You can then add this layout component to your React Router configuration (e.g.: `src/main.tsx`), as a child of the root layout route created in step 1.
+You can then add this layout component to your React Router configuration (e.g.: `src/main.tsx`), as a child of the root layout route created above.
```tsx title="src/main.tsx"
import Layout from './layouts/dashboard';
@@ -135,7 +135,7 @@ export default function OrdersPage() {
}
```
-You can then add these page components as routes to your React Router configuration (e.g.: `src/main.tsx`). By adding them as children of the layout route created in step 2, they will automatically be wrapped with that dashboard layout:
+You can then add these page components as routes to your React Router configuration (e.g.: `src/main.tsx`). By adding them as children of the layout route created above, they are automatically wrapped with that dashboard layout:
```tsx title="src/main.tsx"
import DashboardPage from './pages';