Skip to content

Commit de8fbaa

Browse files
authored
Merge pull request #9131 from marmelab/doc-fix-nextjs-tuto-with-app-router
[Doc] rearrange Next.js tutorial
2 parents 35135ac + 0646284 commit de8fbaa

File tree

4 files changed

+75
-103
lines changed

4 files changed

+75
-103
lines changed

docs/NextJs.md

Lines changed: 75 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,26 @@ Next.js 13 proposes 2 ways to build a React project:
1414

1515
React-admin supports both ways.
1616

17-
## Next.js With Pages Router
17+
## Create a Next.js application
1818

19-
Let's start by creating a new Next.js project called `nextjs-react-admin`.
19+
Use the `create-next-app` package to create a new Next.js project called `next-admin`.
2020

2121
```bash
22-
npx create-next-app@latest next-admin --ts --use-yarn --eslint --no-tailwind --src-dir --no-app --import-alias "@/*"
22+
npx create-next-app@latest
2323
```
2424

25-
![Setup Next.js](./img/nextjs-setup.webp)
25+
A prompt will asks you some questions, feel free to choose answers according to your needs.
26+
This tutorial assumes you're using an `src` folder, so answer 'Yes' to the 5th question. As for the App Router, you can choose to use it or not, this tutorial will explain how to use both.
27+
28+
![Install Next.js with command line](./img/install-next-js-command-line.png)
2629

2730
This creates a project with the following folder structure:
2831

29-
![Basic Architecture Next.js](./img/nextjs-file-structure.png)
32+
| Pages Router | App Router |
33+
|-------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|
34+
| ![ Next Admin folder structure with Pages Router ]( ./img/next-admin-with-page-router-folder-structure.png ) | ![ Next Admin folder structure with App Router ]( ./img/next-admin-with-app-router-folder-structure.png ) |
3035

31-
### Adding React-Admin Dependencies
36+
## Adding React-Admin Dependencies
3237

3338
Add the `react-admin` npm package, as well as a data provider package. In this example, we'll use `ra-data-json-server` to connect to a test API provided by [JSONPlaceholder](https://jsonplaceholder.typicode.com).
3439

@@ -37,86 +42,104 @@ cd next-admin
3742
yarn add react-admin ra-data-json-server
3843
```
3944

40-
### Creating the Admin App Component
45+
## Creating The Admin App Component
4146

42-
Next, create an `admin` directory inside `src`, and the admin App component in `src/admin/App.jsx`:
47+
Next, create a `components` directory inside `src`, and an admin App component in `src/components/AdminApp.jsx`:
4348

4449
```jsx
45-
// in src/admin/App.jsx
46-
import * as React from "react";
47-
import { Admin, Resource, ListGuesser, EditGuesser } from 'react-admin';
48-
import jsonServerProvider from 'ra-data-json-server';
50+
// in src/components/AdminApp.jsx
51+
"use client"; // only needed if you choose App Router
52+
import { Admin, Resource, ListGuesser, EditGuesser } from "react-admin";
53+
import jsonServerProvider from "ra-data-json-server";
4954

50-
const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');
55+
const dataProvider = jsonServerProvider("https://jsonplaceholder.typicode.com");
5156

52-
const App = () => (
57+
const AdminApp = () => (
5358
<Admin dataProvider={dataProvider}>
54-
<Resource name="users" list={ListGuesser} edit={EditGuesser} recordRepresentation="name" />
55-
<Resource name="posts" list={ListGuesser} edit={EditGuesser} recordRepresentation="title" />
59+
<Resource
60+
name="users"
61+
list={ListGuesser}
62+
edit={EditGuesser}
63+
recordRepresentation="name"
64+
/>
65+
<Resource
66+
name="posts"
67+
list={ListGuesser}
68+
edit={EditGuesser}
69+
recordRepresentation="title"
70+
/>
5671
<Resource name="comments" list={ListGuesser} edit={EditGuesser} />
5772
</Admin>
5873
);
5974

60-
export default App;
75+
export default AdminApp;
6176
```
6277

63-
This is a minimal configuration to render CRUD pages for users, posts and comments. React-admin guesses the data structure from the API response.
78+
This is a minimal configuration to render CRUD pages for users, posts and comments. React-admin will guess the fields to display in the list and edition pages based on the API response.
79+
80+
**Tips**: If you choose App Router, do not forget to add [the `"use client"` directive](https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive).
6481

65-
### Exposing the Admin App Component
82+
## Exposing The Admin App Component
6683

67-
Now, let's configure Next.js to render the admin app component in the root path ('/'). Edit the file called `src/pages/index.tsx`, and replace the content with the following:
84+
React-admin is designed as a Single-Page Application, rendered on the client-side. It comes with various client-side only libraries (react-router, emotion, material-ui, react-query). So when you include the `AdminApp` component in the Next.js app, you must prevent Next.js from rendering it on the server.
85+
86+
To do that, import the `<AdminApp>` component in Next.js by using [lazy loading](https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading) and specify the [`ssr` option to false](https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-no-ssr).
87+
88+
The file to modify depends on the router system you chose during setup:
89+
90+
- App Router: `src/app/page.tsx`,
91+
- Pages Router: `src/pages/index.tsx`.
6892

6993
```tsx
70-
// in src/pages/index.tsx
71-
import type, { NextPage } from "next";
94+
import { NextPage } from "next";
7295
import dynamic from "next/dynamic";
96+
const AdminApp = dynamic(() => import("@/components/AdminApp"), { ssr: false });
7397

74-
const App = dynamic(() => import("../admin/App"), { ssr: false });
75-
76-
const Home: NextPage = () => {
77-
return <App />;
78-
};
98+
const Home: NextPage = () => <AdminApp />;
7999

80100
export default Home;
81101
```
82102

83-
**Tip**: Why the dynamic import? React-admin is designed as a Single-Page Application, rendered on the client-side. It comes with its own [routing sytem](./Routing.md), which conflicts with the Next.js routing system. So we must prevent Next.js from rendering the react-admin component on the server-side. Using `dynamic` allows disabling Server-Side Rendering for the `<App>` component.
84-
85103
Now, start the server with `yarn dev`, browse to `http://localhost:3000/`, and you should see the working admin:
86104

87105
![Working Page](./img/nextjs-react-admin.webp)
88106

89-
### Rendering React-Admin In A Sub Route
107+
Starting from there, you can [Add an API](#adding-an-api) as described in the next section, and/or add features to the Next.js app, as explained in the [Getting started tutorial](./Tutorial.md)
108+
109+
## Rendering React-Admin In A Sub Route
90110

91111
In many cases, the admin is only a part of the application. For instance, you may want to render the admin in a subpath, e.g. `/admin`.
92112

93-
Next.js makes it trivial: create a `src/pages/admin.tsx` file with the same content as in the previous section:
113+
This implies the creation of a new page in the Next.js app. Create a new file at the following location:
114+
115+
- App Router: `src/app/admin/page.tsx`
116+
- Pages Router: `src/pages/admin/index.tsx`
117+
118+
No matter which system you choose, the file should contains the same code:
94119

95120
```tsx
96-
// in src/pages/admin.tsx
97-
import type, { NextPage } from "next";
121+
import { NextPage } from "next";
98122
import dynamic from "next/dynamic";
99-
100-
const App = dynamic(() => import("../admin/App"), { ssr: false });
123+
const AdminApp = dynamic(() => import("@/components/AdminApp"), { ssr: false });
101124

102125
const Admin: NextPage = () => {
103-
return <App />;
126+
return <AdminApp />;
104127
};
105128

106129
export default Admin;
107130
```
108131

109-
Now the admin renders at `http://localhost:3000/admin`, and you can use the Next.js routing system to add more pages.
132+
Now the admin renders at `http://localhost:3000/admin`. You can use the Next.js routing system to add more pages - for instance, a frontend app.
133+
134+
**Tip**: If you migrated from the Pages Router, you might have to delete the `.next` directory in your project to ensure NextJS bundles the client dependencies correctly.
110135

111-
### Adding an API
136+
## Adding an API
112137

113138
[Next.js allows to serve an API](https://nextjs.org/docs/api-routes/introduction) from the same server. You *could* use this to build a CRUD API by hand. However, we consider that building a CRUD API on top of a relational database is a solved problem and that developers shouldn't spend time reimplementing it.
114139

115140
For instance, if you store your data in a [PostgreSQL](https://www.postgresql.org/) database, you can use [PostgREST](https://postgrest.org/en/stable/) to expose the data as a REST API with zero configuration. Even better, you can use a Software-as-a-Service like [Supabase](https://supabase.com/) to do that for you.
116141

117-
In such cases, the Next.js API can only serve as a Proxy to authenticate client queries and pass them down to Supabase.
118-
119-
Let's see an example in practice.
142+
In such cases, the Next.js API can serve as a Proxy to authenticate client queries and pass them down to Supabase. Let's see an example in practice.
120143

121144
First, create a Supabase REST API and its associated PostgreSQL database directly on the [Supabase website](https://app.supabase.com/) (it's free for tests and low usage). Once the setup is finished, use the Supabase manager to add the following tables:
122145

@@ -129,17 +152,21 @@ You can populate these tables via the Supabse UI if you want. Supabase exposes a
129152
Copy the Supabase API URL and service role key into Next.js's `.env.local` file:
130153

131154
```sh
132-
# In `.env.local`
155+
# in `.env.local`
133156
SUPABASE_URL="https://MY_INSTANCE.supabase.co"
134157
SUPABASE_SERVICE_ROLE="MY_SERVICE_ROLE_KEY"
135158
```
136159

137160
**Tip**: This example uses the **service role key** here and not the anonymous role. This allows mutations without dealing with authorization. **You shouldn't do this in production**, but use the [Supabase authorization](https://supabase.com/docs/guides/auth) feature instead.
138161

139-
Create [a "catch-all" API route](https://nextjs.org/docs/api-routes/dynamic-api-routes#optional-catch-all-api-routes) in the Next.js app by adding a `pages/api/admin/[[...slug]].ts` file. This API route redirects all calls from the react-admin app to the Supabase CRUD API:
162+
Create [a "catch-all" API route](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#catch-all-segmentss) in the Next.js app by adding a new file at the following location:
163+
164+
- App Router: `src/app/api/admin/[...slug]/page.ts`
165+
- Pages Router: `src/pages/api/admin/[[...slug]].ts`
166+
167+
This API route redirects all calls from the react-admin app to the Supabase CRUD API:
140168

141169
```tsx
142-
// in pages/api/admin/[[...slug]].ts
143170
import { NextApiRequest, NextApiResponse } from "next";
144171

145172
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -180,77 +207,22 @@ yarn add @raphiniert/ra-data-postgrest
180207
```
181208

182209
```jsx
183-
// in src/admin/App.jsx
210+
// in src/components/AdminApp.jsx
184211
import * as React from "react";
185212
import { Admin, Resource, ListGuesser, EditGuesser } from 'react-admin';
186213
import postgrestRestProvider from "@raphiniert/ra-data-postgrest";
187214

188215
const dataProvider = postgrestRestProvider("/api/admin");
189216

190-
const App = () => (
217+
const AdminApp = () => (
191218
<Admin dataProvider={dataProvider}>
192219
<Resource name="users" list={ListGuesser} edit={EditGuesser} recordRepresentation="name" />
193220
<Resource name="posts" list={ListGuesser} edit={EditGuesser} recordRepresentation="title" />
194221
<Resource name="comments" list={ListGuesser} edit={EditGuesser} />
195222
</Admin>
196223
);
197224

198-
export default App;
199-
```
200-
201-
## Next.js With App Router
202-
203-
Let's start by creating a new Next.js project called `nextjs-react-admin` using the new App Router.
204-
205-
```bash
206-
npx create-next-app@latest next-admin --ts --use-yarn --eslint --no-tailwind --no-src-dir --app --import-alias "@/*"
225+
export default AdminApp;
207226
```
208227

209-
This creates a project with the following folder structure:
210-
211-
![Basic Architecture Next.js App Router](./img/nextjs-file-structure-app-router.png)
212-
213-
### Adding React-Admin Dependencies
214-
215-
Add the `react-admin` npm package, as well as a data provider package. In this example, we'll use `ra-data-json-server` to connect to a test API provided by [JSONPlaceholder](https://jsonplaceholder.typicode.com).
216-
217-
```bash
218-
cd next-admin
219-
yarn add react-admin ra-data-json-server
220-
```
221-
222-
### Creating the Admin App Component
223-
224-
Next, replace the `app/page.tsx` file with the following code, which initializes the react-admin app:
225-
226-
```jsx
227-
// in app/page.tsx
228-
"use client";
229-
import { Admin, Resource, ListGuesser, EditGuesser } from "react-admin";
230-
import jsonServerProvider from "ra-data-json-server";
231-
232-
const dataProvider = jsonServerProvider("https://jsonplaceholder.typicode.com");
233-
234-
const App = () => (
235-
<Admin dataProvider={dataProvider}>
236-
<Resource name="users" list={ListGuesser} edit={EditGuesser} recordRepresentation="name" />
237-
<Resource name="posts" list={ListGuesser} edit={EditGuesser} recordRepresentation="title" />
238-
<Resource name="comments" list={ListGuesser} edit={EditGuesser} />
239-
</Admin>
240-
);
241-
242-
export default App;
243-
```
244-
245-
Now, start the server with `yarn dev`, browse to `http://localhost:3000/`, and you should see the working admin:
246-
247-
![Working Page](./img/nextjs-react-admin.webp)
248-
249-
React-admin renders a CRUD for users, posts and comments, guessing the data structure from the API response.
250-
251-
252-
**Tip**: Why the `"use client"` directive? React-admin is designed as a Single-Page Application, rendered on the client-side. It comes with various client-side only libraries (emotion, material-ui, react-query) leveraging the React Context API, and cannot be rendered using React Server components.
253-
254-
**Tip**: If you migrated from the Pages Router, you might have to delete the `.next` directory in your project to ensure NextJS bundles the client dependencies correctly.
255-
256-
Starting from there, you can [Add an API](#adding-an-api) as described in the previous section, and/or add features to the Next.js app, as explained in the [Getting started tutorial](./Tutorial.md)
228+
Your react-admin app now uses the Supabase API to fetch and update data.
92.6 KB
Loading
Loading
Loading

0 commit comments

Comments
 (0)