mirror of https://github.com/abpframework/abp.git
committed by
GitHub
6 changed files with 1034 additions and 509 deletions
File diff suppressed because it is too large
@ -0,0 +1,439 @@ |
|||||
|
```json |
||||
|
//[doc-seo] |
||||
|
{ |
||||
|
"Description": "Learn how to build modern web applications with ABP using React UI — a React-first approach with a dedicated Admin Console, built on Vite, shadcn/ui, Zod, and Axios." |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
# React UI |
||||
|
|
||||
|
## Introduction |
||||
|
|
||||
|
ABP provides a **React UI** option for building modern, client-side web applications. The React UI is part of the **modern template system** and is only available when creating a solution with the `--modern` flag via the [ABP CLI](../../../cli/index.md) or through the **Modern Wizard** in [ABP Studio](../../../studio/index.md). |
||||
|
|
||||
|
> React UI is **not** available in the legacy (non-modern) templates. You must use the modern template system to get a React-based solution. See [Creating a Solution](#creating-a-solution) below. |
||||
|
|
||||
|
The React UI is built on a modern, industry-standard stack: |
||||
|
|
||||
|
| Technology | Purpose | |
||||
|
|---|---| |
||||
|
| [Vite](https://vitejs.dev/) | Build tool and dev server | |
||||
|
| [React](https://react.dev/) | UI framework | |
||||
|
| [shadcn/ui](https://ui.shadcn.com/) | Component library (built on Radix UI + Tailwind CSS) | |
||||
|
| [Zod](https://zod.dev/) | Schema validation | |
||||
|
| [Axios](https://axios-http.com/) | HTTP client | |
||||
|
| [Vitest](https://vitest.dev/) | Unit testing | |
||||
|
| [React Router](https://reactrouter.com/) | Client-side routing | |
||||
|
| [OpenID Connect / OIDC](https://openid.net/connect/) | Authentication (via the ABP Auth Server) | |
||||
|
|
||||
|
## React App and Admin Console |
||||
|
|
||||
|
Every modern React solution consists of two parts: **your React application** and the **ABP Admin Console**. |
||||
|
|
||||
|
### React App (Your Application) |
||||
|
|
||||
|
This is **your application** — the user-facing SPA that you own and customize freely. It comes with: |
||||
|
|
||||
|
- A sample **Books CRUD page** (when `--sample-crud-page` is used) demonstrating how to build a full create/read/update/delete page with the ABP backend |
||||
|
- A plain **Users page** as a minimal reference |
||||
|
- Pre-configured authentication via OIDC against the ABP Auth Server |
||||
|
- Pre-configured HTTP client (Axios) with ABP API integration |
||||
|
|
||||
|
This is where you build your business-specific pages and features. |
||||
|
|
||||
|
The location of the React app differs by template type: |
||||
|
|
||||
|
- **Layered (`app --modern`) and Single-layer (`app-nolayers --modern`)**: the React app lives at the **root of the solution** (alongside the backend projects). |
||||
|
- **Microservice (`microservice --modern`)**: the React app lives at `apps/react/`. |
||||
|
|
||||
|
### React Admin Console (`Volo.Abp.AdminConsole`) |
||||
|
|
||||
|
The **ABP Admin Console** is a pre-built React application that provides all standard ABP module management pages. It is delivered via the `Volo.Abp.AdminConsole` NuGet package and requires no modification on your part — it is updated automatically when you update your ABP packages. |
||||
|
|
||||
|
The Admin Console includes pages for: |
||||
|
|
||||
|
- Users & Roles management |
||||
|
- Organization Units |
||||
|
- Settings |
||||
|
- Audit Logs |
||||
|
- OpenIddict (Application, Scope management) |
||||
|
- Language Management *(if included)* |
||||
|
- Text Template Management *(if included)* |
||||
|
- GDPR *(if included)* |
||||
|
- SaaS / Tenant Management *(if included)* |
||||
|
- And all other optional module pages based on your solution configuration |
||||
|
|
||||
|
How the Admin Console is hosted also differs by template type: |
||||
|
|
||||
|
- **Layered and Single-layer templates**: The `Volo.Abp.AdminConsole` package is added directly to your `*.HttpApi.Host` project. It serves the Admin Console React app at the `/admin-console/*` path of your backend (e.g., `https://localhost:44300/admin-console/`). There is no separate `apps/react-admin-console/` folder — the Admin Console UI is embedded in and served by the backend. |
||||
|
- **Microservice template**: The Admin Console runs as a standalone React app at `apps/react-admin-console/`, served through the Web Gateway (YARP) alongside the main React app. |
||||
|
|
||||
|
In both cases the Admin Console is accessible from the main React app via a navigation link. |
||||
|
|
||||
|
## Creating a Solution |
||||
|
|
||||
|
### Using ABP CLI |
||||
|
|
||||
|
Pass the `--modern` flag to `abp new`: |
||||
|
|
||||
|
````bash |
||||
|
# Layered app with React UI (default when --modern is used) |
||||
|
abp new Acme.BookStore --template app --modern |
||||
|
|
||||
|
# Single-layer app with React UI |
||||
|
abp new Acme.BookStore --template app-nolayers --modern |
||||
|
|
||||
|
# Microservice solution with React UI + React Admin Console |
||||
|
abp new Acme.BookStore --template microservice --modern |
||||
|
```` |
||||
|
|
||||
|
The `react` UI framework is the default when `--modern` is specified. You can also pass it explicitly: |
||||
|
|
||||
|
````bash |
||||
|
abp new Acme.BookStore --template app --modern --ui-framework react |
||||
|
```` |
||||
|
|
||||
|
To create a solution without any UI (API-only backend): |
||||
|
|
||||
|
````bash |
||||
|
abp new Acme.BookStore --template app --modern --ui-framework no-ui |
||||
|
```` |
||||
|
|
||||
|
See the [ABP CLI documentation](../../../cli/index.md#modern-templates) for the full list of options. |
||||
|
|
||||
|
### Using ABP Studio |
||||
|
|
||||
|
Open ABP Studio and use the **New Solution** wizard. Select the **Modern** template variant and choose **React** as the UI framework. The wizard guides you through all available options and generates the solution. |
||||
|
|
||||
|
## Solution Structure |
||||
|
|
||||
|
The layout of the React-related files depends on the template type. |
||||
|
|
||||
|
### Layered and Single-layer Templates |
||||
|
|
||||
|
For `app --modern` and `app-nolayers --modern`, the React app lives at the root of the solution alongside the backend projects. The Admin Console is embedded in the backend via the `Volo.Abp.AdminConsole` NuGet package — there is no separate React Admin Console folder. |
||||
|
|
||||
|
``` |
||||
|
Acme.BookStore/ |
||||
|
├── react/ # Your React application |
||||
|
│ ├── src/ |
||||
|
│ │ ├── pages/ # Your page components |
||||
|
│ │ │ ├── books/ # Sample Books CRUD page |
||||
|
│ │ │ └── users/ # Sample Users page |
||||
|
│ │ ├── components/ # Shared UI components |
||||
|
│ │ ├── lib/ # Utilities, API clients |
||||
|
│ │ ├── hooks/ # Custom React hooks |
||||
|
│ │ └── main.tsx # Entry point |
||||
|
│ ├── public/ |
||||
|
│ │ └── dynamic-env.json # Runtime configuration |
||||
|
│ ├── package.json |
||||
|
│ └── vite.config.ts |
||||
|
├── src/ |
||||
|
│ ├── Acme.BookStore.Application/ |
||||
|
│ ├── Acme.BookStore.Domain/ |
||||
|
│ ├── Acme.BookStore.EntityFrameworkCore/ |
||||
|
│ └── Acme.BookStore.HttpApi.Host/ # Hosts Admin Console at /admin-console/* |
||||
|
└── ... |
||||
|
``` |
||||
|
|
||||
|
### Microservice Template |
||||
|
|
||||
|
For `microservice --modern`, both the React app and the React Admin Console are standalone apps under the `apps/` directory, each served through the Web Gateway (YARP). |
||||
|
|
||||
|
``` |
||||
|
Acme.BookStore/ |
||||
|
├── apps/ |
||||
|
│ ├── react/ # Your React application |
||||
|
│ │ ├── src/ |
||||
|
│ │ │ ├── pages/ |
||||
|
│ │ │ ├── components/ |
||||
|
│ │ │ ├── lib/ |
||||
|
│ │ │ ├── hooks/ |
||||
|
│ │ │ └── main.tsx |
||||
|
│ │ ├── public/ |
||||
|
│ │ │ └── dynamic-env.json # Runtime configuration |
||||
|
│ │ ├── package.json |
||||
|
│ │ └── vite.config.ts |
||||
|
│ ├── react-admin-console/ # ABP Admin Console (managed by ABP) |
||||
|
│ │ ├── public/ |
||||
|
│ │ │ └── dynamic-env.json |
||||
|
│ │ └── ... |
||||
|
│ └── auth-server/ # OpenIddict Auth Server |
||||
|
├── gateways/ |
||||
|
│ └── web/ # YARP reverse proxy for React apps |
||||
|
├── services/ |
||||
|
│ ├── identity/ |
||||
|
│ ├── administration/ |
||||
|
│ └── ... |
||||
|
└── ... |
||||
|
``` |
||||
|
|
||||
|
## Configuration |
||||
|
|
||||
|
### Runtime Configuration (`dynamic-env.json`) |
||||
|
|
||||
|
The React app reads its runtime configuration from `public/dynamic-env.json`. This file is loaded at startup and allows you to change settings without rebuilding the application — useful for different environments (development, staging, production). |
||||
|
|
||||
|
```json |
||||
|
{ |
||||
|
"oAuthConfig": { |
||||
|
"issuer": "https://localhost:44301/", |
||||
|
"clientId": "Acme.BookStore_App", |
||||
|
"scope": "openid profile email offline_access BookStore" |
||||
|
}, |
||||
|
"apis": { |
||||
|
"default": { |
||||
|
"url": "https://localhost:44300", |
||||
|
"rootNamespace": "Acme.BookStore" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
| Key | Description | |
||||
|
|---|---| |
||||
|
| `oAuthConfig.issuer` | URL of the OpenIddict Auth Server | |
||||
|
| `oAuthConfig.clientId` | The OpenIddict client ID registered for this app | |
||||
|
| `oAuthConfig.scope` | OAuth scopes to request | |
||||
|
| `apis.default.url` | Base URL of the backend API | |
||||
|
| `apis.default.rootNamespace` | Root namespace used for API proxy generation | |
||||
|
|
||||
|
For the **microservice** modern template, the `apis.default.url` points to the **Web Gateway** (YARP reverse proxy) instead of a single backend host. |
||||
|
|
||||
|
### Admin Console Configuration |
||||
|
|
||||
|
For the **microservice** template, the Admin Console has its own `dynamic-env.json` (at `apps/react-admin-console/public/dynamic-env.json`) and uses a separate OpenIddict client (`<ProjectName>_AdminConsole`). |
||||
|
|
||||
|
For **layered and single-layer** templates, the Admin Console is embedded in the backend via the `Volo.Abp.AdminConsole` package and does not have a separate configuration file — it inherits its settings from the backend host. |
||||
|
|
||||
|
## Authentication |
||||
|
|
||||
|
Both the React app and the Admin Console authenticate using **OpenID Connect (OIDC)** against the ABP Auth Server (OpenIddict). The auth flow is handled transparently — when a user visits a protected page, they are redirected to the Auth Server login page and returned to the app after successful authentication. All clients use the **Authorization Code flow with PKCE**, which is the recommended flow for SPAs. |
||||
|
|
||||
|
The number of seeded OpenIddict clients depends on the template: |
||||
|
|
||||
|
- **Layered and Single-layer templates**: one client is seeded — `<ProjectName>_App` for the React SPA. The Admin Console is embedded in the backend and shares the same authentication context. |
||||
|
- **Microservice template**: two clients are seeded — `<ProjectName>_App` for the main React SPA and `<ProjectName>_AdminConsole` for the standalone React Admin Console app. |
||||
|
|
||||
|
## Making API Calls |
||||
|
|
||||
|
The React app uses **Axios** as the HTTP client, pre-configured with: |
||||
|
|
||||
|
- The base URL from `dynamic-env.json` |
||||
|
- Automatic Bearer token injection from the OIDC session |
||||
|
- ABP-compatible error handling (reads `error.data` from ABP error responses) |
||||
|
|
||||
|
**Example: Fetching a list of books** |
||||
|
|
||||
|
```typescript |
||||
|
import { useQuery } from '@tanstack/react-query'; |
||||
|
import { apiClient } from '@/lib/api-client'; |
||||
|
|
||||
|
interface BookDto { |
||||
|
id: string; |
||||
|
name: string; |
||||
|
type: number; |
||||
|
publishDate: string; |
||||
|
price: number; |
||||
|
} |
||||
|
|
||||
|
interface PagedResult<T> { |
||||
|
items: T[]; |
||||
|
totalCount: number; |
||||
|
} |
||||
|
|
||||
|
export function useBooks() { |
||||
|
return useQuery({ |
||||
|
queryKey: ['books'], |
||||
|
queryFn: () => |
||||
|
apiClient |
||||
|
.get<PagedResult<BookDto>>('/api/app/book') |
||||
|
.then((res) => res.data), |
||||
|
}); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Localization |
||||
|
|
||||
|
The React app integrates with ABP's [localization system](../../../framework/fundamentals/localization.md). Localization resources are fetched from the backend at startup via the `/api/abp/application-configuration` endpoint and made available throughout the app. |
||||
|
|
||||
|
**Example: Using localization in a component** |
||||
|
|
||||
|
```typescript |
||||
|
import { useLocalization } from '@/hooks/use-localization'; |
||||
|
|
||||
|
export function MyComponent() { |
||||
|
const { l } = useLocalization('BookStore'); |
||||
|
|
||||
|
return <h1>{l('Books')}</h1>; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
The `l(key)` function looks up the key in the loaded localization resources for the given resource name (`'BookStore'` in this example). |
||||
|
|
||||
|
## Authorization |
||||
|
|
||||
|
Permission checks are available via a hook that reads the current user's granted permissions from the ABP application configuration: |
||||
|
|
||||
|
```typescript |
||||
|
import { usePermissions } from '@/hooks/use-permissions'; |
||||
|
|
||||
|
export function BooksPage() { |
||||
|
const { isGranted } = usePermissions(); |
||||
|
|
||||
|
return ( |
||||
|
<div> |
||||
|
{isGranted('BookStore.Books.Create') && ( |
||||
|
<button>Create Book</button> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
Permissions are defined on the server side using ABP's [permission system](../../../framework/fundamentals/authorization/index.md) and are automatically available in the React app. |
||||
|
|
||||
|
## Tech Stack Details |
||||
|
|
||||
|
### shadcn/ui Components |
||||
|
|
||||
|
The React app uses [shadcn/ui](https://ui.shadcn.com/) as the component library. shadcn/ui is not a traditional npm package — components are copied directly into your project under `src/components/ui/`, giving you full ownership and the ability to customize them freely. |
||||
|
|
||||
|
Common components available out of the box include: `Button`, `Input`, `Table`, `Dialog`, `Form`, `Select`, `Tabs`, `Card`, `Badge`, `Dropdown Menu`, and more. |
||||
|
|
||||
|
### Form Validation with Zod |
||||
|
|
||||
|
Forms use [Zod](https://zod.dev/) schemas for validation, integrated with [React Hook Form](https://react-hook-form.com/): |
||||
|
|
||||
|
```typescript |
||||
|
import { z } from 'zod'; |
||||
|
import { useForm } from 'react-hook-form'; |
||||
|
import { zodResolver } from '@hookform/resolvers/zod'; |
||||
|
|
||||
|
const createBookSchema = z.object({ |
||||
|
name: z.string().min(1).max(128), |
||||
|
price: z.number().min(0), |
||||
|
publishDate: z.string(), |
||||
|
}); |
||||
|
|
||||
|
type CreateBookInput = z.infer<typeof createBookSchema>; |
||||
|
|
||||
|
export function CreateBookForm() { |
||||
|
const form = useForm<CreateBookInput>({ |
||||
|
resolver: zodResolver(createBookSchema), |
||||
|
}); |
||||
|
|
||||
|
// ... |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Testing with Vitest |
||||
|
|
||||
|
The React app is pre-configured with [Vitest](https://vitest.dev/) for unit testing: |
||||
|
|
||||
|
```bash |
||||
|
# Run tests |
||||
|
npm run test |
||||
|
|
||||
|
# Run tests with coverage |
||||
|
npm run test:coverage |
||||
|
``` |
||||
|
|
||||
|
## Running the Application |
||||
|
|
||||
|
### Development |
||||
|
|
||||
|
1. Start the backend (from ABP Studio or `dotnet run` in the `*.HttpApi.Host` project). |
||||
|
2. Navigate to the React app directory and start the dev server. |
||||
|
|
||||
|
For **layered and single-layer** templates, the React app is at the solution root: |
||||
|
|
||||
|
````bash |
||||
|
cd react |
||||
|
npm install |
||||
|
npm run dev |
||||
|
```` |
||||
|
|
||||
|
For the **microservice** template, it is under `apps/`: |
||||
|
|
||||
|
````bash |
||||
|
cd apps/react |
||||
|
npm install |
||||
|
npm run dev |
||||
|
```` |
||||
|
|
||||
|
The app will be available at `https://localhost:3000` (or the port configured in `vite.config.ts`). |
||||
|
|
||||
|
3. Access the Admin Console: |
||||
|
- **Layered / Single-layer**: navigate to the backend URL + `/admin-console/` (e.g., `https://localhost:44300/admin-console/`). |
||||
|
- **Microservice**: navigate to the Web Gateway URL + `/admin-console/`. |
||||
|
|
||||
|
### Production Build |
||||
|
|
||||
|
````bash |
||||
|
# Layered / Single-layer |
||||
|
cd react && npm run build |
||||
|
|
||||
|
# Microservice |
||||
|
cd apps/react && npm run build |
||||
|
```` |
||||
|
|
||||
|
The output is placed in `dist/` and can be served by any static file host or CDN. |
||||
|
|
||||
|
## Accessing the Admin Console |
||||
|
|
||||
|
The Admin Console is accessible from within the main React app. After logging in, you will find a link to the Admin Console in the navigation. Clicking it opens the Admin Console, which is served by the backend at `/admin-console/*`. |
||||
|
|
||||
|
The Admin Console provides full management capabilities for: |
||||
|
|
||||
|
- **Identity**: Users, Roles, Claims, Organization Units |
||||
|
- **OpenIddict**: Applications, Scopes |
||||
|
- **Settings**: Application-wide and tenant-level settings |
||||
|
- **Audit Logs**: View and filter audit log entries |
||||
|
- **Language Management** *(optional)* |
||||
|
- **Text Template Management** *(optional)* |
||||
|
- **GDPR** *(optional)* |
||||
|
- **SaaS / Tenant Management** *(optional)* |
||||
|
- **And more**, depending on the modules included in your solution |
||||
|
|
||||
|
## Customization |
||||
|
|
||||
|
### Adding New Pages |
||||
|
|
||||
|
Create a new file under `src/pages/` and register the route in the router configuration: |
||||
|
|
||||
|
```typescript |
||||
|
// src/router.tsx (or similar) |
||||
|
import { BooksPage } from './pages/books/books-page'; |
||||
|
import { MyNewPage } from './pages/my-feature/my-new-page'; |
||||
|
|
||||
|
const routes = [ |
||||
|
{ path: '/books', element: <BooksPage /> }, |
||||
|
{ path: '/my-feature', element: <MyNewPage /> }, |
||||
|
// ... |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
### Adding Menu Items |
||||
|
|
||||
|
Add entries to the navigation configuration to include your new pages in the sidebar or top navigation: |
||||
|
|
||||
|
```typescript |
||||
|
// src/config/navigation.ts (or similar) |
||||
|
export const navigationItems = [ |
||||
|
{ label: 'Books', path: '/books', icon: BookIcon }, |
||||
|
{ label: 'My Feature', path: '/my-feature', icon: StarIcon }, |
||||
|
]; |
||||
|
``` |
||||
|
|
||||
|
### Customizing shadcn/ui Components |
||||
|
|
||||
|
Since shadcn/ui components live in `src/components/ui/`, you can modify them directly. For example, to change the default button variant or add a new variant, edit `src/components/ui/button.tsx`. |
||||
|
|
||||
|
## See Also |
||||
|
|
||||
|
- [ABP CLI — Modern Templates](../../../cli/index.md#modern-templates) |
||||
|
- [ABP Studio](../../../studio/index.md) |
||||
|
- [Localization](../../../framework/fundamentals/localization.md) |
||||
|
- [Authorization / Permissions](../../../framework/fundamentals/authorization/index.md) |
||||
|
- [Auto API Controllers](../../../framework/api-development/auto-controllers.md) |
||||
Loading…
Reference in new issue