Skip to content

Commit

Permalink
[docs] Add a new section for Integration (#4411)
Browse files Browse the repository at this point in the history
  • Loading branch information
prakhargupta1 authored Nov 22, 2024
1 parent 9335afe commit e189558
Show file tree
Hide file tree
Showing 11 changed files with 1,079 additions and 1,067 deletions.
233 changes: 233 additions & 0 deletions docs/data/toolpad/core/integrations/nextjs-approuter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
---
title: Next.js - Integration
---

# Next.js App Router

<p class="description">This guide walks you through adding Toolpad Core to an existing Next.js app.</p>

## 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 (
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
<AppProvider navigation={NAVIGATION} branding={BRANDING}>
{children}
</AppProvider>
</AppRouterCacheProvider>
);
}
```

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 `<head>` and not rendering in the `<body>`.

See the [Material UI Next.js integration docs](https://mui.com/material-ui/integrations/nextjs/) for more details.
:::

## 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 (
<DashboardLayout>
<PageContainer>{props.children}</PageContainer>
</DashboardLayout>
);
}
```

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.

## 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 <Typography>Welcome to a page in the dashboard!</Typography>;
}
```

That's it! You have now integrated Toolpad Core into your Next.js app.

## (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 <Typography>Welcome to the orders page!</Typography>;
}
```

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: <ShoppingCartIcon />,
},
// ...
];
```

## (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, 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
},
},
});
```

### 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 (
<SignInPage
providers={providerMap}
signIn={async (
provider: AuthProvider,
formData: FormData,
callbackUrl?: string,
) => {
'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/)
:::
Loading

0 comments on commit e189558

Please sign in to comment.