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