Open Closed

Question - New React UI (RC) - Guide to add new microservice + frontend pages #10667


User avatar
0
mhoffmeister created

Hello support team,

we started working on our own React frontend for ABP but we've seen that the official React UI for ABP is currently available as a RC. We'd like to switch to the official React UI instead of building our own.

While I created a new solution (with microservices) with the new React UI, I am currently not entirely sure what the usual work flow would be when I want to add a new microservice and new pages to the frontend. Will there be tutorial videos or new guides in the documentation section in the future once the React UI releases?

A full step-by-step guide what to do after the creation of the solution would be really helpful. (from creating a new microservice to adding the new page to the frontend)

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

8 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • For the new official React UI (RC), the current approach is: the React app code lives in your solution and you build your product pages there, while ABP’s built-in administration pages are provided by a separate Admin Console React app (delivered via Volo.Abp.AdminConsole).
    • So, when you add a new microservice, the “usual workflow” is essentially:
      1. Add the microservice to the solution (backend side).
      2. Expose its APIs (application services / HTTP APIs) as usual.
      3. Consume those APIs from your React app by generating/using client/proxy code, then create routes/pages/components in the React app.

    Step-by-step (microservice → React pages)

    1. Create/Add the new microservice

      • Follow the microservice-solution guidance for adding a new microservice to an existing microservice solution (new bounded context/service, its contracts, host, db migrations, etc.).
    2. Make the new microservice reachable from the UI

      • In a microservice solution, you typically access services through the gateway (depending on your setup), so ensure your new service is reachable (routing / reverse proxy config) the same way your existing services are.
    3. Generate client/proxy for the new microservice (then build UI pages)

      • ABP’s documented approach for UI development after adding a microservice is to generate proxies and then start building pages/components for that service.
      • Example from the docs (for Angular and .NET clients; React would follow the same idea of “generate client/proxy then build pages”):
    abp generate-proxy -t csharp -url http://localhost:44333/ -m product --without-contracts
    
     (You’d adapt this idea for your frontend/API client strategy in React, then build routes/pages in the `apps/react/` app.)
    
    1. Add your React pages
      • Add a new route + page component in your React app (under apps/react/), call the new microservice endpoints using the generated client/proxy (or your API layer), then add menu/navigation entries as needed in your React UI.

    About “guides/videos once React UI releases”

    • There is already React UI documentation for ABP 10.4 (RC), and a community article introducing the architecture and what the template contains. More content may come as it moves from RC to stable, but the currently verified sources are below.

    Links (verified):

    • https://abp.io/docs/10.4/framework/ui/react
    • https://abp.io/docs/latest/solution-templates/microservice/adding-new-microservices
    • https://abp.io/community/articles/7rfmgb2v

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    The React UI is still in RC, so there isn't a full end-to-end "add a new microservice + page" tutorial yet. Here's the workflow validated against the 10.4 microservice + React UI template you generated:

    The 3 React apps in your solution

    apps/ contains three independent Vite-based React apps with different roles:

    • apps/react/ — your business SPA. This is where your own pages go.
    • apps/react-admin-console/ — the embedded ABP administration panel served at /admin-console/, wired to the Volo.Abp.AdminConsole package. It hosts the Identity / Setting / SaaS / Audit / OpenIddict / GDPR / AI screens. Don't put your own features here — it's meant to stay as a drop-in admin shell.
    • apps/react-public-web/ — optional Next.js public website (only present when you generated the solution with --public-website).

    Adding a new microservice (backend)

    Use ABP Studio's Add New Microservice wizard (right-click services/ in the solution explorer) or abp new-service on the CLI. The wizard automatically handles:

    • Web gateway appsettings.json — adds the YARP Routes + Clusters entries and appends the new scope to the swagger OAuthScopes(...) list.
    • Identity service OpenIddictDataSeeder.cs — adds CreateScopesAsync("<YourService>"), appends "<YourService>" to the React/AdminConsole client's commonScopes.Union(...), and registers the swagger client scope.

    After the wizard finishes, run the DbMigrator project once so the new OpenIddict scope is seeded into the database.

    The one piece the wizard does NOT do for React (manual step)

    The wizard does not touch apps/react/dynamic-env.json. After it completes, open that file and append your new scope to oAuthConfig.scope:

    {
      "oAuthConfig": {
        "scope": "offline_access openid profile email phone AuthServer IdentityService AdministrationService SaasService AuditLoggingService GdprService LanguageService YourNewService"
      }
    }
    

    Without this, the React app's access token won't include YourNewService and every call to the new endpoints will return 401. Apply the same edit to apps/react-admin-console/dynamic-env.json only if the admin console needs to call the new service.

    Adding a new page in apps/react/

    The frontend follows a hand-written TypeScript API layer pattern (more on that below). Six small files to touch:

    1. API client — src/lib/api/<feature>.ts

    import { api } from './axios'
    
    export interface MyDto { id: string; name?: string }
    export interface CreateUpdateMyDto { name: string }
    export interface PagedResultDto<T> { items: T[]; totalCount: number }
    
    export async function getItems(
      params: { maxResultCount?: number; skipCount?: number; sorting?: string } = {}
    ): Promise<PagedResultDto<MyDto>> {
      const { data } = await api.get<PagedResultDto<MyDto>>('/my-service/my-entity', { params })
      return data
    }
    
    export async function createItem(input: CreateUpdateMyDto): Promise<MyDto> {
      const { data } = await api.post<MyDto>('/my-service/my-entity', input)
      return data
    }
    

    The shared api axios instance (src/lib/api/axios.ts) automatically attaches the Bearer token, the __tenant header for multi-tenancy, Accept-Language from i18next, resolves the baseURL from dynamic-env.json at runtime, redirects to login on 401, and redirects to /403 on 403. Paths are written relative to /api, so '/my-service/my-entity' resolves to https://<gateway>/api/my-service/my-entity through the web gateway. To check the exact controller route for your new service, open Swagger at the service's HTTPS port.

    2. Page — src/pages/<feature>/MyFeaturePage.tsx

    Use TanStack Query for fetching and caching. The most complete sample in the template is BooksPage.tsx — to get the full books CRUD reference (list + create + update + delete + dialog + permissions) regenerate your solution with the -scp (sample CRUD page) flag, then copy the relevant parts.

    import { useQuery } from '@tanstack/react-query'
    import { getItems } from '@/lib/api/my-feature'
    import { usePermissions } from '@/lib/auth/permissions'
    
    export function MyFeaturePage() {
      const { isGranted } = usePermissions()
      const canEdit = isGranted('MyProjectName.MyFeature.Edit')
    
      const { data } = useQuery({
        queryKey: ['items'],
        queryFn: () => getItems({ maxResultCount: 10, skipCount: 0 }),
      })
    
      return <div>{/* render list + form */}</div>
    }
    

    3. Route — src/routes/router.tsx

    import { MyFeaturePage } from '@/pages/my-feature/MyFeaturePage'
    import { createPermissionGuard } from '@/lib/routing/guards'
    
    const myFeatureRoute = createRoute({
      getParentRoute: () => rootRoute,
      path: '/my-feature',
      component: MyFeaturePage,
      beforeLoad: createPermissionGuard('MyProjectName.MyFeature'),
    })
    
    const routeTree = rootRoute.addChildren([
      // ...existing children...
      myFeatureRoute,
    ])
    

    createPermissionGuard reads granted policies from ABP's application-configuration endpoint, so as long as you declare the permission in your backend PermissionDefinitionProvider, the frontend picks it up automatically. There's no separate permission registration on the React side.

    4. Sidebar menu — src/lib/routing/route-config.ts

    import { ListChecks } from 'lucide-react'
    
    export const routeConfig: RouteConfigItem[] = [
      // ...
      {
        path: '/my-feature',
        nameKey: 'Menu:MyFeature',
        icon: ListChecks,
        order: 10,
        requiredPolicy: 'MyProjectName.MyFeature',
      },
    ]
    

    Items with requiredPolicy are hidden automatically when the user lacks that permission.

    5. Localization — src/locales/en.json

    {
      "Menu:MyFeature": "My Feature",
      "::MyFeature": "My Feature"
    }
    

    Key convention matches ABP's localization resource layout: <ResourceName>::<Key> (e.g. AbpUi::SavedSuccessfully, AbpIdentity::Users) for namespaced resources, and ::<Key> for the project's default resource. Menu items use the Menu:<Name> convention.

    6. Button-level permissions inside the page

    const { isGranted } = usePermissions()
    const canEdit = isGranted('MyProjectName.MyFeature.Edit')
    

    Supports compound policies too: isGranted('A || B'), isGranted('A && B').

    Things to watch out for

    • Ports are randomized per abp new. Your .env.development and dynamic-env.json were generated with the actual web gateway / auth server / app ports for this solution. If you change any of them, restart npm run dev — Vite reads the proxy target at startup only.
    • README.md in apps/react/ mentions npm run generate-proxy, but that script is not in package.json. This is a docs/scripts mismatch in the RC template that we're fixing. The API layer is hand-written by design — abp generate-proxy -t js produces vanilla JS proxies that don't plug into the shared axios.ts interceptors (auth, tenant, i18n, 401/403 redirects), so it's only useful as a reference for endpoint shapes.
    • If you generated without -scp, you may see apps/react/src/pages/books/BooksPage.tsx referencing @/lib/api/books which doesn't exist. That's a known template issue when books_sample is off — the BooksPage file should be deleted alongside lib/api/books.ts but currently isn't. You can safely delete apps/react/src/pages/books/ and the dead BookOpen import in route-config.ts. We're fixing this on the template side.
    • The shared axios interceptor handles 401 → login and 403 → /403 for you. To opt out on a specific call, pass skipAuthRedirect: true / skip403Redirect: true in the request config.

    Docs

    • React UI framework docs: https://abp.io/docs/10.4/framework/ui/react
    • Adding new microservices: https://abp.io/docs/latest/solution-templates/microservice/adding-new-microservices
    • React UI architecture introduction: https://abp.io/community/articles/7rfmgb2v
    • The apps/react/README.md in your solution covers the dev/build/test scripts.

    More documentation will land as React UI moves from RC to stable.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    mhoffmeister created

    Hello, thanks for the quick response.

    The step by step guide to add a new page was really helpful. I was now able to create a new microservice, add a new page in the frontend and request data from the backend. :)

    At the start I had some problems with adding a new microservice. When I create a new microservice with the ABP Studio, the helm configuration is set, which results in an error. When I remove the helm configuration it works again. When generating new tables with the ABP Suite I made a mistake where "Create user interface" was still checked, which resulted in errors. Would it be possible to automatically uncheck the checkbox when working with the modern framework / with react?

    Would it also be possible to receive the lib/api/books.ts file for reference? We'd like to experiment some more before we switch to the official React UI / modern framework.

    Are there any plans to implement a way to generate types or DTOs for the frontend? If there are no plans for such a feature, I'll experiment with https://heyapi.dev/ or something similar but wanted to check before I invest time into it.

    Please excuse me for chasing after the new React UI. We are currently in the middle of implementing our own frontend, and the official React UI would save us quite a bit of work.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Glad the workflow worked out for you. Quick answers in order:

    Helm error when adding a microservice

    When you add a microservice via ABP Studio, the wizard runs an "Adjust Helm Config" step only when an etc/helm/ directory exists in the solution (i.e. the solution was generated without --no-kubernetes-configuration). That step performs 6 sub-actions: adjusting build-all-images.ps1, values.<project>-local.yaml, gateways yaml, auth-server yaml, identity yaml, and creating the new Helm chart for the service. Any one of these failing fails the whole wizard.

    Could you share the full error message / stack trace from the failing wizard run (the Studio CLI output, or ~/.abp/studio/logs/)? Without the specific error we can't tell which sub-action broke for you, and that's the info we need to pass to the Studio team for a fix. Your workaround (removing etc/helm/ so the step is skipped) is fine in the meantime if you don't need Helm.

    Suite "Create user interface" default for React projects

    You're right, this should be unchecked automatically. Suite currently doesn't recognize the new React UI — its UiFramework enum only covers MVC, Angular, Blazor (Server / WebApp), and MAUI Blazor, and its ProjectTemplateType doesn't distinguish modern templates from classic. The entity loader defaults ShouldCreateUserInterface to true without any UI-framework check, so when you create an entity in a React solution Suite still tries to generate UI for a framework it doesn't support, and that's what surfaces as the errors you saw.

    Until Suite adds detection for the React UI / modern templates, you'll need to manually uncheck "Create user interface" each time. I'll pass this on to the Suite team so they can default it to unchecked for those projects.

    lib/api/books.ts for reference

    Here you go (this is what the file looks like when you generate with -scp):

    /**
     * Books API - IBookAppService.
     */
    import { api } from './axios'
    
    export enum BookType {
      Undefined = 0,
      Adventure = 1,
      Biography = 2,
      Dystopia = 3,
      Fantastic = 4,
      Horror = 5,
      Science = 6,
      ScienceFiction = 7,
      Poetry = 8,
    }
    
    export const bookTypeOptions: { value: BookType; key: string }[] = [
      { value: BookType.Undefined, key: 'Enum:BookType.0' },
      { value: BookType.Adventure, key: 'Enum:BookType.1' },
      { value: BookType.Biography, key: 'Enum:BookType.2' },
      { value: BookType.Dystopia, key: 'Enum:BookType.3' },
      { value: BookType.Fantastic, key: 'Enum:BookType.4' },
      { value: BookType.Horror, key: 'Enum:BookType.5' },
      { value: BookType.Science, key: 'Enum:BookType.6' },
      { value: BookType.ScienceFiction, key: 'Enum:BookType.7' },
      { value: BookType.Poetry, key: 'Enum:BookType.8' },
    ]
    
    export interface BookDto {
      id: string
      name?: string
      type: BookType
      publishDate?: string
      price: number
      creationTime?: string
    }
    
    export interface CreateUpdateBookDto {
      name: string
      type: BookType
      publishDate: string
      price: number
    }
    
    export interface PagedAndSortedResultRequestDto {
      maxResultCount?: number
      skipCount?: number
      sorting?: string
    }
    
    export interface PagedResultDto<T> {
      items: T[]
      totalCount: number
    }
    
    export async function getBooks(
      params: PagedAndSortedResultRequestDto = {}
    ): Promise<PagedResultDto<BookDto>> {
      const { data } = await api.get<PagedResultDto<BookDto>>('/app/book', {
        params: {
          maxResultCount: params.maxResultCount ?? 10,
          skipCount: params.skipCount ?? 0,
          sorting: params.sorting,
        },
      })
      return data
    }
    
    export async function getBook(id: string): Promise<BookDto> {
      const { data } = await api.get<BookDto>(`/app/book/${id}`)
      return data
    }
    
    export async function createBook(input: CreateUpdateBookDto): Promise<BookDto> {
      const { data } = await api.post<BookDto>('/app/book', input)
      return data
    }
    
    export async function updateBook(
      id: string,
      input: CreateUpdateBookDto
    ): Promise<BookDto> {
      const { data } = await api.put<BookDto>(`/app/book/${id}`, input)
      return data
    }
    
    export async function deleteBook(id: string): Promise<void> {
      await api.delete(`/app/book/${id}`)
    }
    

    The matching backend IBookAppService lives in your BooksService (or whichever service you put the sample in) and uses the standard ABP CRUD app service pattern. The path /app/book is the auto-generated route from BookAppService — adjust to match your own service's route prefix.

    Generating TypeScript types / DTOs from the backend

    There's no official ABP tool for generating TypeScript DTOs/types from your backend at the moment. abp generate-proxy only emits JS (-t js), Angular (-t ng), or C# (-t csharp) proxies — none of which fit the hand-written React API layer pattern.

    What works well with the modern React UI's axios.ts setup is generating from the Swagger / OpenAPI document your backend already exposes. A few options:

    • openapi-typescript (https://github.com/openapi-ts/openapi-typescript) — generates type definitions only, no runtime code. Lightweight and fits the hand-written lib/api/*.ts pattern perfectly. Note the microservice template does NOT expose a single aggregated swagger.json at the gateway — each service has its own schema. The gateway proxies them at /swagger-json/<ClusterId>/swagger/v1/swagger.json (see the *Swagger routes in gateways/web/.../appsettings.json), so you'd run it once per service:
      # via the gateway (single port to remember)
      npx openapi-typescript http://localhost:<gateway-port>/swagger-json/Identity/swagger/v1/swagger.json -o src/lib/api/identity-schema.d.ts
      
      # or directly against each service
      npx openapi-typescript http://localhost:<service-port>/swagger/v1/swagger.json -o src/lib/api/<service>-schema.d.ts
      
      Then reference the generated types in your hand-written service files.
    • swagger-typescript-api (https://github.com/acacode/swagger-typescript-api) — generates both types and a fetch/axios client. More code generated, saves the API call boilerplate.
    • heyapi.dev — also a reasonable choice, similar territory to swagger-typescript-api.

    If you want to keep the hand-written service files and only add types, openapi-typescript is the simplest fit. If you'd rather have a generated client, swagger-typescript-api or heyapi.dev both work.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    mhoffmeister created

    Hello,

    I just realized that with Entity Framework the database defaulted to SQL Server. Is there a way to switch to Postgres (when recreating the solution) via parameter in the ABP CLI while it is not yet available in the ABP Studio?

    For the helm error I'll send the error message and the logs once the error occurs again. I recreated the solution several times while experimenting and it seems like the error doesn't occur anymore. Might have been a mistake on my side, sorry for the inconvenience.

    The rest was really helpful, thanks again. :)

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Yes — the CLI supports the DBMS choice directly on the modern microservice template via -dbms (--database-management-system). Accepted values are sqlserver, mysql, postgresql, oracle, sqlite. So you can run:

    abp new YourSolutionName -t microservice --modern -u react -d ef -dbms postgresql --version 10.4.0-rc.1 <other options as before>
    

    The template renders all the PostgreSQL-specific bits automatically: Npgsql package reference in the service csproj, etc/docker/containers/postgresql.yml docker-compose, the run profile entry under etc/abp-studio/run-profiles/Default.abprun.json, connection strings in each service's appsettings.json, and the AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true) in the affected DbContextFactory files (audit-logging and file-management) for timestamp compatibility. No manual edits needed.

    For the Helm error — thanks for the update, glad it's no longer reproducing. If it shows up again, please share the full Studio CLI output / stack trace and we'll trace it from there.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    mhoffmeister created

    Thanks it works now. :)

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    : )

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.