The Next Generation of Dynamic Component
Préambule Before anything else, I want to clarify that Angular has not changed its way of dynamically creating components. The API has evolved, and what will be shown and explained in this article is still in preview and may change before the release of Angular version 20. This article is based on the angular-next-1 version. Introduction With Angular, there are several ways to dynamically create components and 'attach' them to the view and the change detection cycle. However, until now, some features like setting inputs, or responding to outputs were not feasible on certain APIs (createComponent). This article presents the evolution of the createComponent API with a reminder of the other APIs allowing the dynamic creation of components. NgComponentOutlet: Declarative way to dynamic component creation The NgComponentOutlet directive is a simple way to create components dynamically. It uses a declarative approach. This directive takes several input parameters, one of which is required: the component to be created. @Component({ select: 'app-parent', template: ` Load`, imports: [NgComponentOutlet] }) export class ParentComponent { component: Type | null = null; async loadComponent(): Promise { component = (await import('./hello.component')).HelloComponent; } } The above code represents the implementation of the basic API of the ngComponentOutlet directive. When the button is clicked, the loadComponent method is called. This method lazily loads the HelloComponent component (a little performance is always nice). The instance of the HelloComponent component is then assigned to the component variable. The directive, NgComponentOutlet, which takes as input named ngComponentOutlet the instance of a component, will attach the HostView of the component in the current view. Recently, more specifically Angular version 16, this directive allows inputs to be set automatically in the dynamic component using the ngComponentOutletInputs input This input takes an object that will bind an input of the dynamic component to a value. @Component({ template: `Hello {{ name }}` }) export class HelloComponent { name = input('DevTo'); } @Component({ selector: 'app-parent', template: ` Load`, imports: [NgComponentOutlet] }) export class ParentComponent { component: Type | null = null; async loadComponent(): Promise { component = (await import('./hello.component')).HelloComponent; } } Through this example, the input name is passed to the HelloComponent component. When the HostView of the HelloComponent component is inserted into the current view, the screen will display Hello This is Angular. This method works very well if you know in advance which component is going to be created dynamically. Now let's imagine that the method loadComponent conditionally loads two components: the HelloComponent component taking an input name the AngularComponent component taking no input ... component: Type | null = null; isAngular = signal(false); async loadComponent(): Promise { if(isAngular()) { component = (await import('./angular.component')).AngularComponent; }else { component = (await import('./hello.component')).HelloComponent; } } In the case where component is an AngularComponent instance, the application will not build. An error will be raised explaining that the AngularComponent component must have an input called name even if this component has no use for it. Another sticking point: at the moment it is not possible to react to outputs of the component created dynamically in the parent component. This is where the createComponent function comes in. The update of createComponent function Please note that what follows is based on the Angular-next-1 version and may potentially change in the future. If you would like to take a look at the sources, it's this way: components bindings createComponent function Generally, to create a dynamic component with a more functional approach, we use the createComponent function which we will explain using the ViewContainerRef class. @Component({ select: 'app-parent', template: ` Load`, imports: [NgComponentOutlet] }) export class ParentComponent { dynamicSection = viewChild('dynamicSection', { read: ViewContainerRef } ); async loadComponent(): Promise { const helloComponent = (await import('./hello.component')).HelloComponent this.dynamicSection.createComponent(helloComponent); } } In the above code, a section with a reference named dynamicSection serves as a container for displaying the dynamically created component. When the user clicks on the button, the loadComponent method loads the HelloComponent component, then the createComponent function creates the component, attaches the host view of this component to the current view and, of course, registers this component in the change detection cycle. Thus, whe

Préambule
Before anything else, I want to clarify that Angular has not changed its way of dynamically creating components. The API has evolved, and what will be shown and explained in this article is still in preview and may change before the release of Angular version 20.
This article is based on the angular-next-1 version.
Introduction
With Angular, there are several ways to dynamically create components and 'attach' them to the view and the change detection cycle. However, until now, some features like setting inputs, or responding to outputs were not feasible on certain APIs (createComponent).
This article presents the evolution of the createComponent API with a reminder of the other APIs allowing the dynamic creation of components.
NgComponentOutlet: Declarative way to dynamic component creation
The NgComponentOutlet directive is a simple way to create components dynamically. It uses a declarative approach.
This directive takes several input parameters, one of which is required: the component to be created.
@Component({
select: 'app-parent',
template: `
`,
imports: [NgComponentOutlet]
})
export class ParentComponent {
component: Type<HelloComponent> | null = null;
async loadComponent(): Promise<void> {
component = (await import('./hello.component')).HelloComponent;
}
}
The above code represents the implementation of the basic API of the ngComponentOutlet directive.
When the button is clicked, the loadComponent method is called. This method lazily loads the HelloComponent component (a little performance is always nice). The instance of the HelloComponent component is then assigned to the component variable.
The directive, NgComponentOutlet, which takes as input named ngComponentOutlet the instance of a component, will attach the HostView of the component in the current view.
Recently, more specifically Angular version 16, this directive allows inputs to be set automatically in the dynamic component using the ngComponentOutletInputs input
This input takes an object that will bind an input of the dynamic component to a value.
@Component({
template: `Hello {{ name }}`
})
export class HelloComponent {
name = input('DevTo');
}
@Component({
selector: 'app-parent',
template: `
`,
imports: [NgComponentOutlet]
})
export class ParentComponent {
component: Type<HelloComponent> | null = null;
async loadComponent(): Promise<void> {
component = (await import('./hello.component')).HelloComponent;
}
}
Through this example, the input name is passed to the HelloComponent component. When the HostView of the HelloComponent component is inserted into the current view, the screen will display Hello This is Angular.
This method works very well if you know in advance which component is going to be created dynamically. Now let's imagine that the method loadComponent conditionally loads two components:
- the HelloComponent component taking an input name
- the AngularComponent component taking no input
...
component: Type<HelloComponent | AngularComponent> | null = null;
isAngular = signal(false);
async loadComponent(): Promise<void> {
if(isAngular()) {
component = (await import('./angular.component')).AngularComponent;
}else {
component = (await import('./hello.component')).HelloComponent;
}
}
In the case where component is an AngularComponent instance, the application will not build. An error will be raised explaining that the AngularComponent component must have an input called name even if this component has no use for it.
Another sticking point: at the moment it is not possible to react to outputs of the component created dynamically in the parent component.
This is where the createComponent function comes in.
The update of createComponent function
Please note that what follows is based on the Angular-next-1 version and may potentially change in the future.
If you would like to take a look at the sources, it's this way:
Generally, to create a dynamic component with a more functional approach, we use the createComponent function which we will explain using the ViewContainerRef class.
@Component({
select: 'app-parent',
template: `
`,
imports: [NgComponentOutlet]
})
export class ParentComponent {
dynamicSection = viewChild<ViewContainerRef>('dynamicSection', { read: ViewContainerRef } );
async loadComponent(): Promise<void> {
const helloComponent = (await import('./hello.component')).HelloComponent
this.dynamicSection.createComponent(helloComponent);
}
}
In the above code, a section with a reference named dynamicSection serves as a container for displaying the dynamically created component.
When the user clicks on the button, the loadComponent method loads the HelloComponent component, then the createComponent function creates the component, attaches the host view of this component to the current view and, of course, registers this component in the change detection cycle.
Thus, when the parent component is destroyed, the hook onDestroy of the dynamic component will also be called, thus avoiding any memory leaks.
The createComponent method returns a ComponentRef. This class allows the setInput function to set inputs to the dynamic component. But once again, we cannot react to events from the dynamic component in the parent component.
Today, in Angular version 20, it is possible to go much further than setting inputs. We can
- bind inputs
- bind outputs
- create a two-way binding between parent and child (dynamic)
- apply host directives to the dynamic component and bind inputs and outputs
This function is possible thanks to the standalone component and also to the appearance of signals as Angular's primary reactive system.
@Component({
template: `Hello {{ name }}
`
})
export class HelloComponent {
name = input('DevTo');
refreshName = output<void>();
refresh(): void {
this.refreshName.emit()
}
}
@Component({
select: 'app-parent',
template: `
`,
imports: [NgComponentOutlet]
})
export class ParentComponent {
name = signal('DevTo');
dynamicSection = viewChild<ViewContainerRef>('dynamicSection', { read: ViewContainerRef } );
onRefresh = () => this.name.set('This is Angular');
async loadComponent(): Promise<void> {
const helloComponent = (await import('./hello.component')).HelloComponent
this.dynamicSection.createComponent(helloComponent,{
bindings: [
inputBinding('name', this.name),
outputBinding('refreshName', this.onRefresh),
]
});
}
}
In the above code, the HelloComponent component will be created dynamically by mapping the input name to the value This is Angular and mapping the outPut refreshName to the onRefresh function.
This way, when the user clicks on the Refresh button, the onRefresh function will be called.
Be careful to pass an arrow function so as not to lose the context
Let's go a little further, let's imagine that we want to apply a directive on the dynamic component, this directive will be in charge of adding a class red if the name is “This is Angular”.
With the new definitions of the createComponent function, this scenario is now possible.
@Directive({
selector: '[rainbow]',
host: {
'[class.red]'= 'isRed()';
}
})
export class ColorDirective {
name = input<string>('');
isRed = computed(() => this.name() === 'This is Angular');
}
@Component({
template: `Hello {{ name }}
`
})
export class HelloComponent {
name = input('DevTo');
refreshName = output<void>();
refresh(): void {
this.refreshName.emit()
}
}
@Component({
select: 'app-parent',
template: `
`,
imports: [NgComponentOutlet]
})
export class ParentComponent {
name = signal('DevTo');
dynamicSection = viewChild<ViewContainerRef>('dynamicSection', { read: ViewContainerRef } );
onRefresh = () => this.name.set('This is Angular');
async loadComponent(): Promise<void> {
const helloComponent = (await import('./hello.component')).HelloComponent
this.dynamicSection.createComponent(helloComponent,{
bindings: [
inputBinding('name', () => 'This is Angular'),
outputBinding('refreshName', this.onRefresh),
],
directives: [
{ type: ColorDirective,
bindings: [
inputBinding('name', this.name),
]
}
]
});
}
}
In the example above, the ColorDirective will be applied to the HelloComponent dynamic component by binding the input name.
Initially, the value of the signal name will be DevTo which sets the variable isRed to false
When the user clicks on the refresh button, the onRefresh function will be called, setting the value of the signal name to This is Angular, and thus the variable isRed will have the value true, applying the red css class
Conclusion
With the arrival of Angular 20, creating dynamic components by binding inputs and outputs has never been easier.
This new definition of the createComponent function gives us more freedom in the dynamic creation of components. The use case of dynamically creating a component based on certain conditions becomes much easier, to the point of imagining the possibility of “composing our dynamic components”.
In the future, it will therefore become increasingly easy to create Angular applications by applying the composition pattern.