or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

action-creators.mdfeature-management.mdindex.mdmodule-configuration.mdreducer-creators.mdselectors.mdstandalone-providers.mdstore-service.mdtesting-utilities.md

standalone-providers.mddocs/

0

# Standalone Providers

1

2

Standalone providers enable modern Angular standalone application configuration using environment providers for root and feature state management without traditional NgModules.

3

4

## Capabilities

5

6

### Provide Store

7

8

Configures the root store for standalone Angular applications using environment providers.

9

10

```typescript { .api }

11

/**

12

* Provides root store configuration for standalone applications

13

* @param reducers - Map of reducers or injection token for reducers

14

* @param config - Root store configuration options

15

* @returns EnvironmentProviders for root store setup

16

*/

17

function provideStore<T, V extends Action = Action>(

18

reducers?: ActionReducerMap<T, V> | InjectionToken<ActionReducerMap<T, V>>,

19

config?: RootStoreConfig<T, V>

20

): EnvironmentProviders;

21

```

22

23

**Usage Examples:**

24

25

```typescript

26

import { bootstrapApplication } from "@angular/platform-browser";

27

import { provideStore } from "@ngrx/store";

28

import { AppComponent } from "./app.component";

29

import { reducers, metaReducers } from "./app.reducers";

30

31

// Bootstrap standalone application with store

32

bootstrapApplication(AppComponent, {

33

providers: [

34

provideStore(reducers, {

35

metaReducers,

36

runtimeChecks: {

37

strictStateImmutability: true,

38

strictActionImmutability: true,

39

strictStateSerializability: true,

40

strictActionSerializability: true,

41

strictActionWithinNgZone: true,

42

strictActionTypeUniqueness: true

43

}

44

}),

45

// other providers

46

]

47

});

48

49

// Alternative with injection token

50

export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<AppState>>('Root Reducers');

51

52

bootstrapApplication(AppComponent, {

53

providers: [

54

{ provide: ROOT_REDUCERS, useValue: reducers },

55

provideStore(ROOT_REDUCERS, { metaReducers }),

56

]

57

});

58

```

59

60

### Provide State

61

62

Provides feature state configuration for standalone applications and route-based lazy loading.

63

64

```typescript { .api }

65

/**

66

* Provides feature state with reducer map

67

* @param featureName - Name/key for the feature state slice

68

* @param reducers - Feature reducers map

69

* @param config - Feature store configuration options

70

* @returns EnvironmentProviders for feature state

71

*/

72

function provideState<T, V extends Action = Action>(

73

featureName: string,

74

reducers: ActionReducerMap<T, V> | InjectionToken<ActionReducerMap<T, V>>,

75

config?: StoreConfig<T, V> | InjectionToken<StoreConfig<T, V>>

76

): EnvironmentProviders;

77

78

/**

79

* Provides feature state with single reducer

80

* @param featureName - Name/key for the feature state slice

81

* @param reducer - Single reducer function

82

* @param config - Feature store configuration options

83

* @returns EnvironmentProviders for feature state

84

*/

85

function provideState<T, V extends Action = Action>(

86

featureName: string,

87

reducer: ActionReducer<T, V> | InjectionToken<ActionReducer<T, V>>,

88

config?: StoreConfig<T, V> | InjectionToken<StoreConfig<T, V>>

89

): EnvironmentProviders;

90

91

/**

92

* Provides feature state using feature slice configuration

93

* @param slice - Complete feature slice configuration

94

* @returns EnvironmentProviders for feature state

95

*/

96

function provideState<T, V extends Action = Action>(

97

slice: FeatureSlice<T, V>

98

): EnvironmentProviders;

99

```

100

101

**Usage Examples:**

102

103

```typescript

104

import { provideState } from "@ngrx/store";

105

import { userReducer } from "./user.reducer";

106

import { orderReducer, orderMetaReducer } from "./order.reducer";

107

108

// Route-based feature providers

109

export const userRoutes: Route[] = [

110

{

111

path: '',

112

component: UserListComponent,

113

providers: [

114

// Single reducer feature

115

provideState('users', userReducer, {

116

initialState: { entities: [], loading: false, error: null }

117

})

118

]

119

}

120

];

121

122

export const orderRoutes: Route[] = [

123

{

124

path: '',

125

component: OrderDashboardComponent,

126

providers: [

127

// Reducer map feature

128

provideState('orders', {

129

list: orderListReducer,

130

details: orderDetailsReducer,

131

filters: orderFiltersReducer

132

}, {

133

metaReducers: [orderMetaReducer],

134

initialState: {

135

list: { entities: [], pagination: null },

136

details: null,

137

filters: { status: 'all', dateRange: null }

138

}

139

})

140

]

141

}

142

];

143

144

// Feature slice configuration

145

export const productRoutes: Route[] = [

146

{

147

path: '',

148

component: ProductCatalogComponent,

149

providers: [

150

provideState({

151

name: 'products',

152

reducer: productReducer,

153

initialState: initialProductState,

154

metaReducers: [productCacheMetaReducer]

155

})

156

]

157

}

158

];

159

```

160

161

## Application Configuration Patterns

162

163

### Main Application Setup

164

165

```typescript

166

import { ApplicationConfig } from "@angular/core";

167

import { provideRouter } from "@angular/router";

168

import { provideStore } from "@ngrx/store";

169

import { routes } from "./app.routes";

170

import { reducers, metaReducers } from "./store";

171

172

export const appConfig: ApplicationConfig = {

173

providers: [

174

provideRouter(routes),

175

provideStore(reducers, {

176

metaReducers,

177

runtimeChecks: {

178

strictStateImmutability: true,

179

strictActionImmutability: true,

180

strictActionWithinNgZone: true

181

}

182

}),

183

// Additional providers

184

provideHttpClient(),

185

provideAnimations()

186

]

187

};

188

189

// Bootstrap application

190

import { bootstrapApplication } from "@angular/platform-browser";

191

import { AppComponent } from "./app.component";

192

193

bootstrapApplication(AppComponent, appConfig);

194

```

195

196

### Route-Based Lazy Loading

197

198

```typescript

199

import { Routes } from "@angular/router";

200

import { provideState } from "@ngrx/store";

201

202

export const routes: Routes = [

203

{

204

path: 'dashboard',

205

loadComponent: () => import('./dashboard/dashboard.component').then(c => c.DashboardComponent),

206

providers: [

207

provideState('dashboard', dashboardReducer, {

208

initialState: () => ({

209

widgets: getDefaultWidgets(),

210

layout: getUserLayout(),

211

preferences: getUserDashboardPreferences()

212

})

213

})

214

]

215

},

216

{

217

path: 'analytics',

218

loadChildren: () => import('./analytics/analytics.routes').then(r => r.analyticsRoutes),

219

providers: [

220

// Shared analytics state

221

provideState('analytics', analyticsReducer)

222

]

223

},

224

{

225

path: 'settings',

226

loadComponent: () => import('./settings/settings.component').then(c => c.SettingsComponent),

227

providers: [

228

provideState({

229

name: 'settings',

230

reducer: settingsReducer,

231

initialState: defaultSettings,

232

metaReducers: [settingsValidationMetaReducer]

233

})

234

]

235

}

236

];

237

```

238

239

### Feature-Specific Routes

240

241

```typescript

242

// analytics.routes.ts

243

import { Routes } from "@angular/router";

244

import { provideState } from "@ngrx/store";

245

246

export const analyticsRoutes: Routes = [

247

{

248

path: '',

249

component: AnalyticsOverviewComponent

250

},

251

{

252

path: 'reports',

253

component: ReportsComponent,

254

providers: [

255

// Feature-specific state for reports

256

provideState('reports', reportsReducer, {

257

initialState: {

258

templates: [],

259

generated: [],

260

filters: defaultReportFilters

261

}

262

})

263

]

264

},

265

{

266

path: 'metrics',

267

component: MetricsComponent,

268

providers: [

269

// Metrics-specific state

270

provideState('metrics', {

271

realtime: realtimeMetricsReducer,

272

historical: historicalMetricsReducer,

273

alerts: alertsReducer

274

})

275

]

276

}

277

];

278

```

279

280

## Advanced Configuration

281

282

### Injectable Configuration

283

284

```typescript

285

import { InjectionToken, inject } from "@angular/core";

286

import { UserService } from "./user.service";

287

288

// Injectable configuration tokens

289

export const USER_INITIAL_STATE = new InjectionToken<UserState>('User Initial State', {

290

providedIn: 'root',

291

factory: () => {

292

const userService = inject(UserService);

293

return {

294

profile: userService.getCachedProfile(),

295

preferences: userService.getStoredPreferences(),

296

permissions: userService.getPermissions()

297

};

298

}

299

});

300

301

export const USER_META_REDUCERS = new InjectionToken<MetaReducer<UserState>[]>('User Meta Reducers', {

302

providedIn: 'root',

303

factory: () => [

304

userAuditMetaReducer,

305

userValidationMetaReducer

306

]

307

});

308

309

// Route using injectable configuration

310

export const userRoutes: Routes = [

311

{

312

path: '',

313

component: UserProfileComponent,

314

providers: [

315

provideState('user', userReducer, {

316

initialState: USER_INITIAL_STATE,

317

metaReducers: USER_META_REDUCERS

318

})

319

]

320

}

321

];

322

```

323

324

### Dynamic State Registration

325

326

```typescript

327

import { inject } from "@angular/core";

328

import { Store } from "@ngrx/store";

329

330

// Service for dynamic state management

331

export class DynamicStateService {

332

private store = inject(Store);

333

334

registerFeature<T>(name: string, reducer: ActionReducer<T>, initialState?: T) {

335

// Add reducer dynamically

336

this.store.addReducer(name, reducer);

337

338

// Initialize state if provided

339

if (initialState) {

340

this.store.dispatch({ type: `[${name}] Initialize`, payload: initialState });

341

}

342

}

343

344

unregisterFeature(name: string) {

345

this.store.removeReducer(name);

346

}

347

}

348

349

// Component using dynamic state

350

@Component({

351

selector: 'app-dynamic-feature',

352

providers: [DynamicStateService]

353

})

354

export class DynamicFeatureComponent {

355

private dynamicState = inject(DynamicStateService);

356

357

ngOnInit() {

358

// Register state when component initializes

359

this.dynamicState.registerFeature('temporaryFeature', temporaryReducer, {

360

data: [],

361

loading: false

362

});

363

}

364

365

ngOnDestroy() {

366

// Clean up when component is destroyed

367

this.dynamicState.unregisterFeature('temporaryFeature');

368

}

369

}

370

```

371

372

### Environment-Based Configuration

373

374

```typescript

375

// environment.ts

376

export const environment = {

377

production: false,

378

storeConfig: {

379

runtimeChecks: {

380

strictStateImmutability: true,

381

strictActionImmutability: true,

382

strictStateSerializability: true,

383

strictActionSerializability: true,

384

strictActionWithinNgZone: true,

385

strictActionTypeUniqueness: true

386

},

387

metaReducers: ['logger', 'freeze']

388

}

389

};

390

391

// environment.prod.ts

392

export const environment = {

393

production: true,

394

storeConfig: {

395

runtimeChecks: {

396

strictStateImmutability: false,

397

strictActionImmutability: false,

398

strictStateSerializability: false,

399

strictActionSerializability: false,

400

strictActionWithinNgZone: false,

401

strictActionTypeUniqueness: false

402

},

403

metaReducers: []

404

}

405

};

406

407

// app.config.ts

408

import { environment } from "../environments/environment";

409

410

const getMetaReducers = (): MetaReducer<AppState>[] => {

411

const reducers: MetaReducer<AppState>[] = [];

412

413

if (environment.storeConfig.metaReducers.includes('logger')) {

414

reducers.push(storeLogger);

415

}

416

417

if (environment.storeConfig.metaReducers.includes('freeze')) {

418

reducers.push(storeFreeze);

419

}

420

421

return reducers;

422

};

423

424

export const appConfig: ApplicationConfig = {

425

providers: [

426

provideStore(reducers, {

427

runtimeChecks: environment.storeConfig.runtimeChecks,

428

metaReducers: getMetaReducers()

429

})

430

]

431

};

432

```

433

434

## Integration with Angular Features

435

436

### HTTP Client Integration

437

438

```typescript

439

import { provideHttpClient, withInterceptors } from "@angular/common/http";

440

import { provideStore } from "@ngrx/store";

441

442

export const appConfig: ApplicationConfig = {

443

providers: [

444

provideHttpClient(withInterceptors([authInterceptor, errorInterceptor])),

445

provideStore(reducers, { metaReducers }),

446

// State that depends on HTTP client

447

provideState('auth', authReducer, {

448

initialState: () => ({

449

user: getStoredUser(),

450

token: getStoredToken(),

451

refreshToken: getStoredRefreshToken()

452

})

453

})

454

]

455

};

456

```

457

458

### Router Integration

459

460

```typescript

461

import { provideRouter, withPreloading, PreloadAllModules } from "@angular/router";

462

import { provideStore, provideState } from "@ngrx/store";

463

464

export const appConfig: ApplicationConfig = {

465

providers: [

466

provideRouter(routes, withPreloading(PreloadAllModules)),

467

provideStore(reducers),

468

// Router-dependent state

469

provideState('navigation', navigationReducer, {

470

initialState: {

471

currentRoute: null,

472

previousRoute: null,

473

breadcrumbs: []

474

}

475

})

476

]

477

};

478

```

479

480

## Best Practices

481

482

1. **Route-Level Providers**: Use `provideState` in route providers for lazy-loaded features

483

2. **Application-Level Setup**: Configure root store in main application config

484

3. **Feature Isolation**: Keep feature state providers close to their consuming components

485

4. **Dynamic Registration**: Use dynamic state registration for truly dynamic features

486

5. **Environment Configuration**: Adapt store configuration based on environment

487

6. **Injection Tokens**: Use injection tokens for complex, service-dependent initial states