diff --git a/examples/core-tutorial/.eslintrc.json b/examples/core-tutorial/.eslintrc.json
new file mode 100644
index 00000000000..bffb357a712
--- /dev/null
+++ b/examples/core-tutorial/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "next/core-web-vitals"
+}
diff --git a/examples/core-tutorial/.gitignore b/examples/core-tutorial/.gitignore
new file mode 100644
index 00000000000..0563835d349
--- /dev/null
+++ b/examples/core-tutorial/.gitignore
@@ -0,0 +1,36 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+.yarn/install-state.gz
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
\ No newline at end of file
diff --git a/examples/core-tutorial/app/(dashboard)/layout.tsx b/examples/core-tutorial/app/(dashboard)/layout.tsx
new file mode 100644
index 00000000000..945ea792377
--- /dev/null
+++ b/examples/core-tutorial/app/(dashboard)/layout.tsx
@@ -0,0 +1,6 @@
+import * as React from 'react';
+import { DashboardLayout } from '@toolpad/core/DashboardLayout';
+
+export default function Layout(props: { children: React.ReactNode }) {
+ return {props.children};
+}
diff --git a/examples/core-tutorial/app/(dashboard)/page/page.tsx b/examples/core-tutorial/app/(dashboard)/page/page.tsx
new file mode 100644
index 00000000000..9a9f1baea40
--- /dev/null
+++ b/examples/core-tutorial/app/(dashboard)/page/page.tsx
@@ -0,0 +1,13 @@
+import { Typography, Container } from '@mui/material';
+
+export default function Home() {
+ return (
+
+
+
+ Dashboard content!
+
+
+
+ );
+}
diff --git a/examples/core-tutorial/app/api/auth/[...nextAuth]/route.ts b/examples/core-tutorial/app/api/auth/[...nextAuth]/route.ts
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/examples/core-tutorial/app/auth/[...path]/page.tsx b/examples/core-tutorial/app/auth/[...path]/page.tsx
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/examples/core-tutorial/app/layout.tsx b/examples/core-tutorial/app/layout.tsx
new file mode 100644
index 00000000000..20373eade60
--- /dev/null
+++ b/examples/core-tutorial/app/layout.tsx
@@ -0,0 +1,28 @@
+import { AppProvider } from '@toolpad/core/AppProvider';
+import DashboardIcon from '@mui/icons-material/Dashboard';
+import type { Navigation } from '@toolpad/core';
+import theme from '../theme';
+
+const NAVIGATION: Navigation = [
+ {
+ kind: 'header',
+ title: 'Main items',
+ },
+ {
+ slug: '/page',
+ title: 'Page',
+ icon: ,
+ },
+];
+
+export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/examples/core-tutorial/app/page.tsx b/examples/core-tutorial/app/page.tsx
new file mode 100644
index 00000000000..4f3a9b7609b
--- /dev/null
+++ b/examples/core-tutorial/app/page.tsx
@@ -0,0 +1,26 @@
+import Link from 'next/link';
+import { Button, Container, Typography, Box } from '@mui/material';
+
+export default function Home() {
+ return (
+
+
+
+ Welcome to Toolpad Core!
+
+
+
+ Get started by editing (dashboard)/page/page.tsx
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/examples/core-tutorial/next-env.d.ts b/examples/core-tutorial/next-env.d.ts
new file mode 100644
index 00000000000..4f11a03dc6c
--- /dev/null
+++ b/examples/core-tutorial/next-env.d.ts
@@ -0,0 +1,5 @@
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/examples/core-tutorial/next.config.mjs b/examples/core-tutorial/next.config.mjs
new file mode 100644
index 00000000000..f26ac370c56
--- /dev/null
+++ b/examples/core-tutorial/next.config.mjs
@@ -0,0 +1,3 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {};
+export default nextConfig;
diff --git a/examples/core-tutorial/package.json b/examples/core-tutorial/package.json
new file mode 100644
index 00000000000..117477fedae
--- /dev/null
+++ b/examples/core-tutorial/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "core-test",
+ "version": "0.1.0",
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "react": "^18",
+ "react-dom": "^18",
+ "next": "^14",
+ "@toolpad/core": "latest",
+ "@mui/material": "^5",
+ "@mui/icons-material": "^5",
+ "@emotion/react": "^11",
+ "@emotion/styled": "^11",
+ "@emotion/cache": "^11"
+ },
+ "devDependencies": {
+ "typescript": "^5",
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "eslint": "^8",
+ "eslint-config-next": "^14"
+ }
+}
diff --git a/examples/core-tutorial/theme.ts b/examples/core-tutorial/theme.ts
new file mode 100644
index 00000000000..354fcbdd88f
--- /dev/null
+++ b/examples/core-tutorial/theme.ts
@@ -0,0 +1,115 @@
+'use client';
+import { createTheme } from '@mui/material/styles';
+
+const defaultTheme = createTheme();
+
+const theme = createTheme(defaultTheme, {
+ palette: {
+ background: {
+ default: defaultTheme.palette.grey['50'],
+ },
+ },
+ typography: {
+ h6: {
+ fontWeight: '700',
+ },
+ },
+ components: {
+ MuiAppBar: {
+ styleOverrides: {
+ root: {
+ borderWidth: 0,
+ borderBottomWidth: 1,
+ borderStyle: 'solid',
+ borderColor: defaultTheme.palette.divider,
+ boxShadow: 'none',
+ },
+ },
+ },
+ MuiList: {
+ styleOverrides: {
+ root: {
+ padding: 0,
+ },
+ },
+ },
+ MuiIconButton: {
+ styleOverrides: {
+ root: {
+ color: defaultTheme.palette.primary.dark,
+ padding: 8,
+ },
+ },
+ },
+ MuiListSubheader: {
+ styleOverrides: {
+ root: {
+ color: defaultTheme.palette.grey['600'],
+ fontSize: 12,
+ fontWeight: '700',
+ height: 40,
+ paddingLeft: 32,
+ },
+ },
+ },
+ MuiListItem: {
+ styleOverrides: {
+ root: {
+ paddingTop: 0,
+ paddingBottom: 0,
+ },
+ },
+ },
+ MuiListItemButton: {
+ styleOverrides: {
+ root: {
+ borderRadius: 8,
+ '&.Mui-selected': {
+ '& .MuiListItemIcon-root': {
+ color: defaultTheme.palette.primary.dark,
+ },
+ '& .MuiTypography-root': {
+ color: defaultTheme.palette.primary.dark,
+ },
+ '& .MuiSvgIcon-root': {
+ color: defaultTheme.palette.primary.dark,
+ },
+ '& .MuiTouchRipple-child': {
+ backgroundColor: defaultTheme.palette.primary.dark,
+ },
+ },
+ '& .MuiSvgIcon-root': {
+ color: defaultTheme.palette.action.active,
+ },
+ },
+ },
+ },
+ MuiListItemText: {
+ styleOverrides: {
+ root: {
+ '& .MuiTypography-root': {
+ fontWeight: '500',
+ },
+ },
+ },
+ },
+ MuiListItemIcon: {
+ styleOverrides: {
+ root: {
+ minWidth: 34,
+ },
+ },
+ },
+ MuiDivider: {
+ styleOverrides: {
+ root: {
+ borderBottomWidth: 2,
+ marginLeft: '16px',
+ marginRight: '16px',
+ },
+ },
+ },
+ },
+});
+
+export default theme;
diff --git a/examples/core-tutorial/tsconfig.json b/examples/core-tutorial/tsconfig.json
new file mode 100644
index 00000000000..e7ff90fd276
--- /dev/null
+++ b/examples/core-tutorial/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}