Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Doc] Add Next.js app router install instructions #8965

Merged
merged 3 commits into from
Jun 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 76 additions & 11 deletions docs/NextJs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@ title: "Next.Js Integration"

React-admin runs seamlessly on [Next.js](https://nextjs.org/), with minimal configuration.

## Setting Up Next.js
Next.js 13 proposes 2 ways to build a React project:

- the classic [Pages router](https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts),
- the new [App router](https://vercel.com/blog/nextjs-app-router-data-fetching) with React Server components.

React-admin supports both ways.

This chapter documents first the classic "Pages router" way, as it's the most common (and stable) one.

If you want to use the new "App router", please refer to the [Setting Up Next.js With App Router](#setting-up-nextjs-with-app-router) later in this chapter.

## Setting Up Next.js With Pages Router

Let's start by creating a new Next.js project called `nextjs-react-admin`.

Expand All @@ -26,6 +37,7 @@ This creates a project with the following folder structure:
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).

```bash
cd next-admin
yarn add react-admin ra-data-json-server
```

Expand All @@ -34,24 +46,23 @@ Next, create the admin app component in `src/admin/App.jsx`:
```jsx
// in src/admin/App.jsx
import * as React from "react";
import { Admin, Resource, ListGuesser } from 'react-admin';
import { Admin, Resource, ListGuesser, EditGuesser } from 'react-admin';
import jsonServerProvider from 'ra-data-json-server';

const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');

const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="posts" list={ListGuesser} />
<Resource name="comments" list={ListGuesser} />
<Resource name="users" list={ListGuesser} edit={EditGuesser} recordRepresentation="name" />
<Resource name="posts" list={ListGuesser} edit={EditGuesser} recordRepresentation="title" />
<Resource name="comments" list={ListGuesser} edit={EditGuesser} />
</Admin>
);

export default App;
```

This is a minimal admin for 2 resources. React-admin should be able to render a list of posts and a list of comments, guessing the data structure from the API response.

## Using React-Admin As The Root Application
This is a minimal configuration to render CRUD pages for users, posts and comments. React-admin guesses the data structure from the API response.

Now, let's configure Next.js to render the admin app component in the root path ('/'). Edit the file called `pages/index.tsx`, and replace the content with the following:

Expand Down Expand Up @@ -109,12 +120,13 @@ Let's see an example in practice.

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:

- `users` with fields: `id`, `name`, and `email`
- `posts` with fields: `id`, `title`, and `body`
- `comments` with fields: `id`, `name`, `body`, and `postId` (a foreign key to the `posts.id` field)

You can populate these tables via the Supabse UI if you want. Supabase exposes a REST API at `https://YOUR_INSTANCE.supabase.co/rest/v1`.

Copy the supabase API URL and service role key into Next.js's `.env.local` file:
Copy the Supabase API URL and service role key into Next.js's `.env.local` file:

```sh
# In `.env.local`
Expand Down Expand Up @@ -170,17 +182,70 @@ yarn add @raphiniert/ra-data-postgrest
```jsx
// in src/admin/App.jsx
import * as React from "react";
import { Admin, Resource, ListGuesser } from 'react-admin';
import { Admin, Resource, ListGuesser, EditGuesser } from 'react-admin';
import postgrestRestProvider from "@raphiniert/ra-data-postgrest";

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

const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="posts" list={ListGuesser} />
<Resource name="comments" list={ListGuesser} />
<Resource name="users" list={ListGuesser} edit={EditGuesser} recordRepresentation="name" />
<Resource name="posts" list={ListGuesser} edit={EditGuesser} recordRepresentation="title" />
<Resource name="comments" list={ListGuesser} edit={EditGuesser} />
</Admin>
);

export default App;
```

## Setting Up Next.js With App Router

Let's start by creating a new Next.js project called `nextjs-react-admin` using the new App Router.

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

This creates a project with the following folder structure:

![Basic Architecture Next.js App Router](./img/nextjs-file-structure-app-router.png)

## Setting Up React-Admin

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).

```bash
cd next-admin
yarn add react-admin ra-data-json-server
```

Next, replace the `app/pages.tsx` file with the following code, which initializes the react-admin app:

```jsx
// in app/pages.tsx
"use client";
import { Admin, Resource, ListGuesser, EditGuesser } from "react-admin";
import jsonServerProvider from "ra-data-json-server";

const dataProvider = jsonServerProvider("https://jsonplaceholder.typicode.com");

const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={ListGuesser} edit={EditGuesser} recordRepresentation="name" />
<Resource name="posts" list={ListGuesser} edit={EditGuesser} recordRepresentation="title" />
<Resource name="comments" list={ListGuesser} edit={EditGuesser} />
</Admin>
);

export default App;
```

Now, start the server with `yarn dev`, browse to `http://localhost:3000/`, and you should see the working admin:

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

React-admin renders a CRUD for users, posts and comments, guessing the data structure from the API response.

**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.

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](./GettingStarted.md)
Binary file added docs/img/nextjs-file-structure-app-router.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/img/nextjs-react-admin.webp
Binary file not shown.
11 changes: 4 additions & 7 deletions packages/ra-core/src/core/CoreAdminContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
LegacyDataProvider,
} from '../types';

const defaultStore = memoryStore();

export interface CoreAdminContextProps {
authProvider?: AuthProvider | LegacyAuthProvider;
basename?: string;
Expand All @@ -45,9 +47,9 @@ export const CoreAdminContext = (props: CoreAdminContextProps) => {
const {
authProvider,
basename,
dataProvider,
dataProvider = defaultDataProvider,
i18nProvider,
store,
store = defaultStore,
children,
history,
queryClient,
Expand Down Expand Up @@ -100,8 +102,3 @@ React-admin requires a valid dataProvider function to work.`);
</AuthContext.Provider>
);
};

CoreAdminContext.defaultProps = {
Copy link
Member Author

@fzaninotto fzaninotto Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next.js 13 comes with React 18, which logs a warning for defaultProps in development. I fixed a few of these warnings, but we should definitely do the rest in another PR.

dataProvider: defaultDataProvider,
store: memoryStore(),
};
4 changes: 0 additions & 4 deletions packages/ra-core/src/core/CoreAdminRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,6 @@ export const CoreAdminRoutes = (props: CoreAdminRoutesProps) => {
);
};

CoreAdminRoutes.defaultProps = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one was logging a warning AND was unused

customRoutes: [],
};

export interface CoreAdminRoutesProps extends CoreLayoutProps {
layout: LayoutComponent;
catchAll: CatchAllComponent;
Expand Down
28 changes: 17 additions & 11 deletions packages/ra-ui-materialui/src/AdminUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,28 @@ import {
} from './layout';
import { Login, AuthCallback } from './auth';

export const AdminUI = ({ notification, ...props }: AdminUIProps) => (
export const AdminUI = ({
layout = DefaultLayout,
catchAll = NotFound,
loading = LoadingPage,
loginPage = Login,
authCallbackPage = AuthCallback,
notification = Notification,
...props
}: AdminUIProps) => (
<ScopedCssBaseline enableColorScheme>
<CoreAdminUI {...props} />
<CoreAdminUI
layout={layout}
catchAll={catchAll}
loading={loading}
loginPage={loginPage}
authCallbackPage={authCallbackPage}
{...props}
/>
{createElement(notification)}
</ScopedCssBaseline>
);

export interface AdminUIProps extends CoreAdminUIProps {
notification?: ComponentType;
}

AdminUI.defaultProps = {
layout: DefaultLayout,
catchAll: NotFound,
loading: LoadingPage,
loginPage: Login,
authCallbackPage: AuthCallback,
notification: Notification,
};
10 changes: 3 additions & 7 deletions packages/react-admin/src/Admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { CoreAdminProps, localStorageStore } from 'ra-core';
import { AdminUI, AdminContext, RaThemeOptions } from 'ra-ui-materialui';

import { defaultI18nProvider } from './defaultI18nProvider';
const defaultStore = localStorageStore();

/**
* Main admin component, entry point to the application.
Expand Down Expand Up @@ -98,7 +99,7 @@ export const Admin = (props: AdminProps) => {
dataProvider,
disableTelemetry,
history,
i18nProvider,
i18nProvider = defaultI18nProvider,
layout,
loading,
loginPage,
Expand All @@ -107,7 +108,7 @@ export const Admin = (props: AdminProps) => {
notification,
queryClient,
requireAuth,
store,
store = defaultStore,
ready,
theme,
lightTheme,
Expand Down Expand Up @@ -156,11 +157,6 @@ export const Admin = (props: AdminProps) => {
);
};

Admin.defaultProps = {
i18nProvider: defaultI18nProvider,
store: localStorageStore(),
};

export default Admin;

export interface AdminProps extends CoreAdminProps {
Expand Down