diff --git a/docs/data/toolpad/core/introduction/TutorialDefault.tsx b/docs/data/toolpad/core/introduction/TutorialDefault.tsx index 350f676d6cf..ad0edf09d63 100644 --- a/docs/data/toolpad/core/introduction/TutorialDefault.tsx +++ b/docs/data/toolpad/core/introduction/TutorialDefault.tsx @@ -6,7 +6,7 @@ import { AppProvider } from '@toolpad/core/AppProvider'; import { DashboardLayout } from '@toolpad/core/DashboardLayout'; import { PageContainer } from '@toolpad/core/PageContainer'; import { useDemoRouter } from '@toolpad/core/internals'; -import type { Navigation } from '@toolpad/core'; +import type { Navigation } from '@toolpad/core/AppProvider'; const NAVIGATION: Navigation = [ { diff --git a/docs/data/toolpad/core/introduction/TutorialPages.tsx b/docs/data/toolpad/core/introduction/TutorialPages.tsx index d5c698cbc04..95017fe1b97 100644 --- a/docs/data/toolpad/core/introduction/TutorialPages.tsx +++ b/docs/data/toolpad/core/introduction/TutorialPages.tsx @@ -8,7 +8,7 @@ import { AppProvider } from '@toolpad/core/AppProvider'; import { DashboardLayout } from '@toolpad/core/DashboardLayout'; import { useDemoRouter } from '@toolpad/core/internals'; import { PageContainer } from '@toolpad/core/PageContainer'; -import type { Navigation } from '@toolpad/core'; +import type { Navigation } from '@toolpad/core/AppProvider'; const NAVIGATION: Navigation = [ { diff --git a/docs/data/toolpad/core/introduction/integration.md b/docs/data/toolpad/core/introduction/integration.md index 874f8326f77..fda56d5534b 100644 --- a/docs/data/toolpad/core/introduction/integration.md +++ b/docs/data/toolpad/core/introduction/integration.md @@ -133,29 +133,13 @@ npm install next-auth@beta ```ts title="auth.ts" import NextAuth from 'next-auth'; import GitHub from 'next-auth/providers/github'; -import Credentials from 'next-auth/providers/credentials'; import type { Provider } from 'next-auth/providers'; + const providers: Provider[] = [ GitHub({ clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, }), - Credentials({ - credentials: { - email: { label: 'Email Address', type: 'email' }, - password: { label: 'Password', type: 'password' }, - }, - authorize(c) { - if (c.password !== 'password') { - return null; - } - return { - id: 'test', - name: 'Test User', - email: String(c.email), - }; - }, - }), ]; export const providerMap = providers.map((provider) => { @@ -187,12 +171,6 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ }); ``` -:::warning - -This file is only for demonstration purposes and allows signing in with `password` as the password. You should use a more secure method for authentication in a production environment, preferably OAuth with your own `CLIENT_ID` and `CLIENT_SECRET`. Find more details on to get these values in the [Auth.js documentation](https://authjs.dev/guides/configuring-github). - -::: - #### 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`: @@ -216,10 +194,6 @@ export default function SignIn() { 'use server'; try { return await signIn(provider.id, { - ...(formData && { - email: formData.get('email'), - password: formData.get('password'), - }), redirectTo: callbackUrl ?? '/', }); } catch (error) { @@ -235,10 +209,7 @@ export default function SignIn() { // Handle Auth.js errors if (error instanceof AuthError) { return { - error: - error.type === 'CredentialsSignin' - ? 'Invalid credentials.' - : 'An error with Auth.js occurred.', + error: error.message, type: error.type, }; } @@ -281,6 +252,10 @@ That's it! You now have Toolpad Core integrated into your Next.js App Router app {{"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: @@ -290,23 +265,15 @@ To integrate Toolpad Core into your Next.js Pages Router app, follow these steps In your root layout file (e.g., `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 { DashboardLayout } from '@toolpad/core/DashboardLayout'; 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 { createTheme } from '@mui/material/styles'; - -export type NextPageWithLayout

= NextPage & { - getLayout?: (page: React.ReactElement) => React.ReactNode; - requireAuth?: boolean; -}; - -type AppPropsWithLayout = AppProps & { - Component: NextPageWithLayout; -}; +import type { Navigation } from '@toolpad/core/AppProvider'; const NAVIGATION: Navigation = [ { @@ -318,55 +285,25 @@ const NAVIGATION: Navigation = [ title: 'Dashboard', icon: , }, - { - segment: 'orders', - title: 'Orders', - icon: , - }, ]; const BRANDING = { title: 'My Toolpad Core App', }; -const lightTheme = createTheme(); - -const darkTheme = createTheme({ palette: { mode: 'dark' } }); - -const theme = { - light: lightTheme, - dark: darkTheme, -}; - -export default theme; - -export default function AppLayout({ Component, pageProps }: AppPropsWithLayout) { +export default function App({ Component }: { Component: React.ElementType }) { return ( - + - - {children} + + + + + + - - ); -} - -export default function App(props: AppPropsWithLayout) { - const { - Component, - pageProps: { session, ...pageProps }, - } = props; - - const getLayout = Component.getLayout ?? getDefaultLayout; - - let pageContent = getLayout(); - pageContent = {pageContent}; - - return ( - - {pageContent} ); } @@ -474,7 +411,6 @@ npm install next-auth@beta ```ts title="auth.ts" import NextAuth from 'next-auth'; import GitHub from 'next-auth/providers/github'; -import Credentials from 'next-auth/providers/credentials'; import type { Provider } from 'next-auth/providers'; const providers: Provider[] = [ @@ -482,22 +418,6 @@ const providers: Provider[] = [ clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, }), - Credentials({ - credentials: { - email: { label: 'Email Address', type: 'email' }, - password: { label: 'Password', type: 'password' }, - }, - authorize(c) { - if (c.password !== 'password') { - return null; - } - return { - id: 'test', - name: 'Test User', - email: String(c.email), - }; - }, - }), ]; export const providerMap = providers.map((provider) => { @@ -529,12 +449,6 @@ export const { handlers, auth } = NextAuth({ }); ``` -:::warning - -This file is only for demonstration purposes and allows signing in with `password` as the password. You should use a more secure method for authentication in a production environment, preferably OAuth with your own `CLIENT_ID` and `CLIENT_SECRET`. Find more details on to get these values in the [Auth.js documentation](https://authjs.dev/guides/configuring-github). - -::: - #### c. Modify `_app.tsx` Modify `_app.tsx` to include the `authentication` prop and other helpers: @@ -550,10 +464,9 @@ 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'; +import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; import LinearProgress from '@mui/material/LinearProgress'; -import theme from '../theme'; export type NextPageWithLayout

= NextPage & { getLayout?: (page: React.ReactElement) => React.ReactNode; @@ -620,7 +533,6 @@ function AppLayout({ children }: { children: React.ReactNode }) { branding={BRANDING} session={session} authentication={AUTHENTICATION} - theme={theme} > {children} @@ -673,33 +585,16 @@ export default function SignIn({ providers={providers} signIn={async (provider, formData, callbackUrl) => { try { - const signInResponse = await signIn( - provider.id, - formData - ? { - email: formData.get('email') as string, - password: formData.get('password') as string, - redirect: false, - } - : { callbackUrl: callbackUrl ?? '/' }, - ); + const signInResponse = await signIn(provider.id, { + callbackUrl: callbackUrl ?? '/', + }); if (signInResponse && signInResponse.error) { // Handle Auth.js errors return { - error: - signInResponse.error === 'CredentialsSignin' - ? 'Invalid credentials' - : 'An error with Auth.js occurred', + error: signInResponse.error.message, type: signInResponse.error, }; } - // If the sign in was successful, - // manually redirect to the callback URL - // since the `redirect: false` option was used - // to be able to display error messages on the same page without a full page reload - if (provider.id === 'credentials') { - router.push(callbackUrl ?? '/'); - } return {}; } catch (error) { // An error boundary must exist to handle unknown errors @@ -767,3 +662,7 @@ export const config = { 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/examples/core-auth-nextjs-pages-nextauth-4/src/pages/_app.tsx b/examples/core-auth-nextjs-pages-nextauth-4/src/pages/_app.tsx index 086b2b6700a..b8e40df7033 100644 --- a/examples/core-auth-nextjs-pages-nextauth-4/src/pages/_app.tsx +++ b/examples/core-auth-nextjs-pages-nextauth-4/src/pages/_app.tsx @@ -8,7 +8,7 @@ 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'; +import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; import LinearProgress from '@mui/material/LinearProgress'; diff --git a/examples/core-auth-nextjs-pages/src/pages/_app.tsx b/examples/core-auth-nextjs-pages/src/pages/_app.tsx index c8c87cbf85e..e2642586973 100644 --- a/examples/core-auth-nextjs-pages/src/pages/_app.tsx +++ b/examples/core-auth-nextjs-pages/src/pages/_app.tsx @@ -8,7 +8,7 @@ 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'; +import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; import LinearProgress from '@mui/material/LinearProgress'; diff --git a/examples/core-auth-nextjs/src/app/layout.tsx b/examples/core-auth-nextjs/src/app/layout.tsx index 8bdfd54b70f..610583ab933 100644 --- a/examples/core-auth-nextjs/src/app/layout.tsx +++ b/examples/core-auth-nextjs/src/app/layout.tsx @@ -3,7 +3,7 @@ import { AppProvider } from '@toolpad/core/nextjs'; import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter'; import DashboardIcon from '@mui/icons-material/Dashboard'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; -import type { Navigation } from '@toolpad/core'; +import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut } from 'next-auth/react'; import { auth } from '../auth'; diff --git a/examples/core-tutorial/app/layout.tsx b/examples/core-tutorial/app/layout.tsx index aa9c0899cb6..eff87342084 100644 --- a/examples/core-tutorial/app/layout.tsx +++ b/examples/core-tutorial/app/layout.tsx @@ -2,7 +2,7 @@ import { AppProvider } from '@toolpad/core/nextjs'; import DashboardIcon from '@mui/icons-material/Dashboard'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter'; -import type { Navigation } from '@toolpad/core'; +import type { Navigation } from '@toolpad/core/AppProvider'; import theme from '../theme'; const NAVIGATION: Navigation = [ diff --git a/packages/create-toolpad-app/src/templates/nextjs-app/rootLayout.ts b/packages/create-toolpad-app/src/templates/nextjs-app/rootLayout.ts index 66a12de55d4..e229705445e 100644 --- a/packages/create-toolpad-app/src/templates/nextjs-app/rootLayout.ts +++ b/packages/create-toolpad-app/src/templates/nextjs-app/rootLayout.ts @@ -8,7 +8,7 @@ import { AppProvider } from '@toolpad/core/nextjs'; import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter'; import DashboardIcon from '@mui/icons-material/Dashboard'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; -import type { Navigation } from '@toolpad/core'; +import type { Navigation } from '@toolpad/core/AppProvider'; ${ authEnabled ? `import { SessionProvider, signIn, signOut } from 'next-auth/react'; diff --git a/packages/create-toolpad-app/src/templates/nextjs-pages/app.ts b/packages/create-toolpad-app/src/templates/nextjs-pages/app.ts index a44133cacc0..02915f140f7 100644 --- a/packages/create-toolpad-app/src/templates/nextjs-pages/app.ts +++ b/packages/create-toolpad-app/src/templates/nextjs-pages/app.ts @@ -13,7 +13,7 @@ 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'; +import type { Navigation } from '@toolpad/core/AppProvider'; ${ authEnabled ? `import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; diff --git a/playground/nextjs-pages/next-env.d.ts b/playground/nextjs-pages/next-env.d.ts index 09392b58d09..725dd6f2451 100644 --- a/playground/nextjs-pages/next-env.d.ts +++ b/playground/nextjs-pages/next-env.d.ts @@ -3,4 +3,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/playground/nextjs-pages/src/pages/_app.tsx b/playground/nextjs-pages/src/pages/_app.tsx index 60697f1aaa1..4efcb23d5d7 100644 --- a/playground/nextjs-pages/src/pages/_app.tsx +++ b/playground/nextjs-pages/src/pages/_app.tsx @@ -8,7 +8,7 @@ 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'; +import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut, useSession } from 'next-auth/react'; import LinearProgress from '@mui/material/LinearProgress'; diff --git a/playground/nextjs/src/app/layout.tsx b/playground/nextjs/src/app/layout.tsx index 96a76406401..e0e8ad5c3ba 100644 --- a/playground/nextjs/src/app/layout.tsx +++ b/playground/nextjs/src/app/layout.tsx @@ -3,7 +3,7 @@ import { AppProvider } from '@toolpad/core/nextjs'; import { AppRouterCacheProvider } from '@mui/material-nextjs/v14-appRouter'; import DashboardIcon from '@mui/icons-material/Dashboard'; import ShoppingCartIcon from '@mui/icons-material/ShoppingCart'; -import type { Navigation } from '@toolpad/core'; +import type { Navigation } from '@toolpad/core/AppProvider'; import { SessionProvider, signIn, signOut } from 'next-auth/react'; import { auth } from '../auth'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f53dccbec81..ded9efe1f1b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,7 +195,7 @@ importers: version: 7.37.1(eslint@8.57.1) eslint-plugin-react-compiler: specifier: latest - version: 0.0.0-experimental-45ae4c3-20241011(eslint@8.57.1) + version: 0.0.0-experimental-fa06e2c-20241014(eslint@8.57.1) eslint-plugin-react-hooks: specifier: 4.6.2 version: 4.6.2(eslint@8.57.1) @@ -2013,7 +2013,7 @@ packages: resolution: {integrity: sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/core': ^7.25.8 '@babel/preset-typescript@7.25.7': resolution: {integrity: sha512-rkkpaXJZOFN45Fb+Gki0c+KMIglk4+zZXOoMJuyEK8y8Kkc8Jd3BDmP7qPsz0zQMJj+UD7EprF+AqAXcILnexw==} @@ -2083,7 +2083,7 @@ packages: '@docsearch/react@3.6.2': resolution: {integrity: sha512-rtZce46OOkVflCQH71IdbXSFK+S8iJZlUF56XBW5rIgx/eG5qoomC7Ag3anZson1bBac/JFQn7XOBfved/IMRA==} peerDependencies: - '@types/react': '>= 16.8.0 < 19.0.0' + '@types/react': ^18.3.11 react: '>= 16.8.0 < 19.0.0' react-dom: '>= 16.8.0 < 19.0.0' search-insights: '>= 1 < 3' @@ -2899,7 +2899,7 @@ packages: resolution: {integrity: sha512-P0E7ZrxOuyYqBvVv9w8k7wm+Xzx/KRu+BGgFcR2htTsGCpJNQJCSUXNUZ50MUmSU9hzqhwbQWNXhV1MBTl6F7A==} engines: {node: '>=14.0.0'} peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 + '@types/react': ^18.3.11 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 peerDependenciesMeta: @@ -5991,8 +5991,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-plugin-react-compiler@0.0.0-experimental-45ae4c3-20241011: - resolution: {integrity: sha512-m+BmeFtVWzrHt87sb5g5jLttHdo9YScPiuiingdEqLYtUv7pdVi6pQgY3nCOI4h09C4wmWS9xzpaVNEgiODOBg==} + eslint-plugin-react-compiler@0.0.0-experimental-fa06e2c-20241014: + resolution: {integrity: sha512-tHntZz8Kx/6RgCLn7aDGfBQizqTUUfHEDaBcrvJi1GhKzgDxmAbdn85Y6z8eGSh4s0gufNWyO9WRCYLf0hP0ow==} engines: {node: ^14.17.0 || ^16.0.0 || >= 18.0.0} peerDependencies: eslint: '>=7' @@ -15780,7 +15780,7 @@ snapshots: globals: 13.24.0 rambda: 7.5.0 - eslint-plugin-react-compiler@0.0.0-experimental-45ae4c3-20241011(eslint@8.57.1): + eslint-plugin-react-compiler@0.0.0-experimental-fa06e2c-20241014(eslint@8.57.1): dependencies: '@babel/core': 7.25.8 '@babel/parser': 7.25.8