Aviary
Guides

Multi-App (forFeature)

Run multiple independent Inertia apps inside the same NestJS process using forFeature.

Register a scope

// admin.module.ts
import { Module } from '@nestjs/common';
import { InertiaModule } from '@dudousxd/nestjs-inertia';

@Module({
  imports: [
    InertiaModule.forFeature({
      scope: 'admin',
      rootView: 'inertia/admin/root.html',
      vite: { entry: 'admin/client.tsx' },
      share: async (req) => ({ adminUser: req.user }),
    }),
  ],
  controllers: [AdminDashboardController],
})
export class AdminModule {}

Each scope gets its own InertiaService, Vite manifest, SSR loader, and shared props. Completely isolated from the default scope registered by forRoot().

Async scope registration

InertiaModule.forFeatureAsync({
  scope: 'admin',
  imports: [ConfigModule],
  useFactory: (config: ConfigService) => ({
    rootView: config.get('ADMIN_ROOT_VIEW'),
    vite: { entry: 'admin/client.tsx' },
    share: async (req) => ({ adminUser: req.user }),
  }),
  inject: [ConfigService],
})

Supports useFactory, useClass, and useExisting, same as forRootAsync.

Select a scope in a controller

Use @UseInertia('scope-name') on a controller or handler to bind it to that scope.

import { Controller, Get } from '@nestjs/common';
import { Inertia, UseInertia } from '@dudousxd/nestjs-inertia';

@Controller('admin')
@UseInertia('admin')
export class AdminDashboardController {
  @Get('/')
  @Inertia('admin/Dashboard')
  index() {
    return { stats: { users: 120 } };
  }
}

Default scope

Controllers without @UseInertia use the default scope registered by InertiaModule.forRoot(). No decorator needed.

Shared props are scope-isolated

The share function from forRoot() does not run for forFeature scopes. The share function from a forFeature scope does not run for the default scope or other feature scopes.

Each scope is a separate Inertia universe: its own root HTML shell, its own Vite manifest, its own shared props factory.

On this page