# ng-dynamic-component renders a component type at runtime and still wires its inputs

> ng-dynamic-component is an Angular library that renders a component chosen at runtime through an ndc-dynamic element, and adds a directive layer so that a dynamically created component still receives inputs and emits outputs. It suits applications that build their UI from configuration, and its sharp edge is that the standalone import gives you the component but not the input and output wiring.

**gund/ng-dynamic-component** — Dynamic components with full life-cycle support for inputs and outputs for Angular

- Repository: https://github.com/gund/ng-dynamic-component
- Website: https://malkevich-alex.gitbook.io/ng-dynamic-component/
- Stars: 559 · Forks: 62
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/gund-ng-dynamic-component

## Angular 22 removed the factory the library used to lean on

The reason this library exists is written into its compatibility note. Angular 22 and later require ng-dynamic-component v11 or later because Angular removed ComponentFactoryResolver and ComponentFactory. Those two APIs were the standard way to instantiate a component from its type at runtime, and when they went, every library that wrapped them had to change. The library's stated purpose is dynamic components with full life-cycle support for inputs and outputs for Angular, and the detail that matters is the second half, because rendering a component at runtime is only half the problem. A dynamically created component still has input bindings that need values and output events that need listeners, and if the runtime-created instance is wired the same way as a template-declared one, then the usual Angular patterns keep working. The repository is an Nx workspace with jest tests and a goldens directory, and the released package is built from the projects directory and packed with a script that copies the licence, readme, changelog and code of conduct alongside the build output.

## Three ways to render, and they are not equivalent

The usage section documents three distinct approaches, and the differences between them are where the real decisions live. The first is the DynamicModule approach. You import DynamicModule into your NgModule, and then include an ndc-dynamic element in your template with the component type bound to it.

```ts
import { DynamicModule } from 'ng-dynamic-component';

@NgModule({
  imports: [DynamicModule],
})
export class MyModule {}
```

The binding itself is a plain property holding the component class.

```ts
@Component({
  selector: 'my-component',
  template: ` <ndc-dynamic [ndcDynamicComponent]="component"></ndc-dynamic> `,
})
class MyComponent {
  component = Math.random() > 0.5 ? MyDynamicComponent1 : MyDynamicComponent2;
}
```

The second is the standalone version of the same element, which the documentation marks as available since v10.7.0.

```ts
import { DynamicComponent } from 'ng-dynamic-component';

@Component({
  selector: 'my-component',
  template: ` <ndc-dynamic [ndcDynamicComponent]="component"></ndc-dynamic> `,
  imports: [DynamicComponent],
  standalone: true,
})
class MyComponent {
  component = Math.random() > 0.5 ? MyDynamicComponent1 : MyDynamicComponent2;
}
```

The third is using the NgComponentOutlet directive from @angular/common instead of the ndc-dynamic element, and it is the route that carries the input and output wiring.

## The standalone import gives you the element but not the wiring

This is the caveat the documentation states most plainly, and it will waste an afternoon if you miss it. Importing DynamicComponent into a standalone component imports only the ndc-dynamic element and nothing else, so things like dynamic inputs and outputs will not work, and you have to import them separately. If you still need both the ndc-dynamic element and dynamic inputs and outputs, the recommendation is to keep using the DynamicModule API. That is a real constraint rather than a stylistic note, so the two routes pull in different things: the standalone import gives the element alone, while the module brings both. The library's own naming reflects the split, with the input and output support living in a separate module and a separate directive from the element itself, which is a sensible decomposition but one that means the consumer has to know which piece they need.

## Dynamic inputs and outputs ride on a separate module and directive

For the NgComponentOutlet route, the wiring comes from DynamicIoModule, which you import into your NgModule.

```ts
import { DynamicIoModule } from 'ng-dynamic-component';

@NgModule({
  imports: [DynamicIoModule],
})
export class MyModule {}
```

You then apply ndcDynamicInputs and ndcDynamicOutputs to the outlet.

```ts
@Component({
  selector: 'my-component',
  template: `<ng-template [ngComponentOutlet]="component"
                           [ndcDynamicInputs]="inputs"
                           [ndcDynamicOutputs]="outputs"
                           ></ng-template>`
})
class MyComponent {
  component = MyDynamicComponent1;
  inputs = {...};
  outputs = {...};
}
```

The same thing can be written with the structural directive syntax, where the inputs and outputs are passed as additional expressions on the ngComponentOutlet.

```ts
@Component({
  selector: 'my-component',
  template: `<ng-container *ngComponentOutlet="component;
                            ndcDynamicInputs: inputs;
                            ndcDynamicOutputs: outputs"
                            ></ng-container>`
})
class MyComponent {
  component = MyDynamicComponent1;
  inputs = {...};
  outputs = {...};
}
```

Both forms take the same shape, a component type plus a map of inputs and a map of outputs, and the second is the more compact of the two.

## Standalone usage of the outlet needs the directive added by hand

The standalone form of the NgComponentOutlet route arrived with v10.7.0 alongside the standalone element, and it works differently. You import ComponentOutletInjectorModule from the library and use the structural directive syntax with the inputs and outputs in the template.

```ts
import { ComponentOutletInjectorModule } from 'ng-dynamic-component';

@Component({
  selector: 'my-component',
  template: `<ng-container *ngComponentOutlet="component;
                            ndcDynamicInputs: inputs;
                            ndcDynamicOutputs: outputs"
                            ></ng-container>`
  imports: [ComponentOutletInjectorModule],
  standalone: true,
})
class MyComponent {
  component = MyDynamicComponent1;
  inputs = {...};
  outputs = {...};
}
```

The documentation then adds the part that trips people up. If you want to use standard dynamic inputs and outputs with ngComponentOutlet as a standalone API, you need to add the DynamicIoDirective to your imports, so the import list becomes the module plus the directive.

```ts
import { DynamicIoDirective, ComponentOutletInjectorModule } from 'ng-dynamic-component';
```

So the standalone path for the outlet needs two imports where the module path needs one, which is the cost of not using NgModules.

## Signal based components live in a separate package

There is a boundary around what this package covers, and it is stated in one line. If you want to dynamically render signal based components, the documentation points you at the signal-component-io package, described as a separate project under projects/ng-dynamic-component/signal-component-io with its own readme, and marks that capability as available since v10.8.0. The distinction follows from Angular's own history, since signals and the old decorator based inputs and outputs are different mechanisms, and a library that bridges runtime instantiation to the older decorator based bindings does not automatically bridge to signals. If your components are signal based, you are installing two packages rather than one, and you need to check which of the two carries the input and output support you actually need. The release list shows 10.8.1 and 10.8.2 in January 2025, followed by a gap to 11.0.0 in July 2026, so the signal support arrived just before the Angular 22 jump.

## The version table is the part to read before you install

The compatibility table is long and it is the most useful thing in the readme. For Angular 14.1.3 and later the library is at 11.x.x with the npm range ng-dynamic-component@^11.0.0, and the same Angular range down to below 22 uses 10.3.1 with ng-dynamic-component@^10.3.1. Below that, Angular 14.0.0 to below 22 uses 10.2.x, Angular 13 uses 10.1.x with the tilde range ~10.1.0, Angular 12 uses 9.x.x, and the table continues down through Angular 11 with 8.x.x, Angular 10 with 7.x.x, Angular 9 with 6.x.x, Angular 8 with 5.x.x, Angular 7 with 4.x.x, Angular 6 with 3.x.x, Angular 5 with 2.x.x, Angular 4 with 1.x.x and Angular 2 with 0.x.x. The pattern is that the major version tracks the Angular major, and the practical consequence is that an application on an older Angular pins an older library rather than fighting the newest one. The repository's release list shows v10.8.1 and v10.8.2 alongside v11.0.0, and the npm badge and the semantic-release badge suggest the publishing flow is automated from commit messages.

## Where ng-dynamic-component is the wrong choice

The first wrong case is an application that could use a conditional in its template. If the choice between two components is a compile-time decision in your own code, a switch on a property in an ng-container does the same job with no runtime instantiation, no injector wiring and no extra dependency, and it keeps the type information that a component type held in a variable loses. The second wrong case is a project that has already adopted standalone components everywhere and wants dynamic inputs and outputs without touching NgModules, because the documentation itself says to keep using the DynamicModule API for that combination, which means the library is asking you to keep a module around. The third is a team on Angular 22 and later that has not accepted the breaking change, since the library had to change its own major version to keep working at all, which is a fair signal about how the platform area is moving. The fourth is an application whose dynamic components are signal based, since that support is in a separate package and the boundary between the two is not obvious from the top level readme. The fifth is a codebase that has already standardised on NgComponentOutlet with the standard inputs and outputs, where the extra library buys nothing because plain Angular handles the static case well and the library exists for the dynamic case.

## Conclusion

Teams building Angular applications whose interface is composed from configuration rather than from templates, and who need those runtime components to keep their inputs and outputs working, are the people this library is for, and the reason it still exists is that Angular removed the machinery it used to rely on. It is the wrong choice for an application that could use a switch statement in a template, since a runtime component adds a layer for a case a conditional can cover. Check three things before you adopt it: which major version of the library matches your Angular version, since the compatibility table runs from Angular 2 through 22 and the 11.x line is the one that supports Angular 22 and later, that your application uses NgModules rather than only standalone components, because the standalone import of the component does not bring the dynamic inputs and outputs with it, and that the signal based components you want to render are covered by the separate signal-component-io package rather than this one. The library is MIT licensed, the newest release is v11.0.0 from 2026-07-17, and the last push was on the same day.

## FAQ

### What problem does ng-dynamic-component solve in Angular?

It lets an Angular application render a component type chosen at runtime, and it keeps the full life-cycle support for inputs and outputs that a template-declared component would have. It exists because Angular 22 and later removed ComponentFactoryResolver and ComponentFactory, so ng-dynamic-component v11 is required on those versions.

### Which version of ng-dynamic-component should I install?

Match the library major to your Angular major. The table lists 11.x.x for Angular 14.1.3 and later, 10.3.1 for Angular 14.1.3 up to below 22, 10.2.x for 14.0.0 up to below 22, 10.1.x for Angular 13, 9.x.x for Angular 12, and continues down to 0.x.x for Angular 2.

### How do I install ng-dynamic-component?

With npm install ng-dynamic-component --save. The library is published to npm under the name ng-dynamic-component, and the repository is an Nx workspace built with nx build ng-dynamic-component and tested with jest.

### What is the difference between DynamicModule and the standalone import?

Importing the standalone DynamicComponent gives you only the ndc-dynamic element and nothing else, so dynamic inputs and outputs will not work and have to be imported separately. The documentation recommends keeping the DynamicModule API if you need both the element and dynamic inputs and outputs.

### How do I render a component with dynamic inputs and outputs?

Import DynamicIoModule into your NgModule, then apply ndcDynamicInputs and ndcDynamicOutputs to an ngComponentOutlet, either as template bindings on an ng-template or as expressions in the structural directive form. A standalone component needs the ComponentOutletInjectorModule plus the DynamicIoDirective added to its imports.

## Sources

- [gund/ng-dynamic-component on GitHub](https://github.com/gund/ng-dynamic-component)
- [License: MIT](https://github.com/gund/ng-dynamic-component/blob/master/LICENSE)
- [Project website](https://malkevich-alex.gitbook.io/ng-dynamic-component/)
- [README](https://github.com/gund/ng-dynamic-component/blob/master/README.md)
- [Releases](https://github.com/gund/ng-dynamic-component/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gund-ng-dynamic-component
