angular/angular-cli · error · SchematicsException

Prerequisite for application shell is to define a router-out

Error message

Prerequisite for application shell is to define a router-outlet in your root component.

What it means

The app-shell schematic requires the root (bootstrap) component's template to contain a `<router-outlet>` because the generated application shell renders routes through the router. `validateProject` checks the template with a regex and throws a SchematicsException if no router-outlet exists.

Source

Thrown at packages/schematics/angular/app-shell/index.ts:131

    .map((imp) => {
      const pathStringLiteral = imp.moduleSpecifier as ts.StringLiteral;

      return pathStringLiteral.text;
    })[0];

  return join(dirname(bootstrappingFilePath), componentRelativeFilePath + '.ts');
}
// end helper functions.

function validateProject(mainPath: string): Rule {
  return (host: Tree) => {
    const routerOutletCheckRegex = /<router-outlet.*?>([\s\S]*?)(?:<\/router-outlet>)?/;

    const componentPath = getBootstrapComponentPath(host, mainPath);
    const tmpl = getComponentTemplateInfo(host, componentPath);
    const template = getComponentTemplate(host, componentPath, tmpl);
    if (!routerOutletCheckRegex.test(template)) {
      throw new SchematicsException(
        `Prerequisite for application shell is to define a router-outlet in your root component.`,
      );
    }
  };
}

function getMetadataProperty(metadata: ts.Node, propertyName: string): ts.PropertyAssignment {
  const properties = (metadata as ts.ObjectLiteralExpression).properties;
  const property = properties.filter(ts.isPropertyAssignment).filter((prop) => {
    const name = prop.name;
    switch (name.kind) {
      case ts.SyntaxKind.Identifier:
        return name.getText() === propertyName;
      case ts.SyntaxKind.StringLiteral:
        return name.text === propertyName;
    }

    return false;

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Add `<router-outlet></router-outlet>` to the root component's template (found via getBootstrapComponentPath from the main entry)
  2. Run `ng add @angular/router` / set up routing first, then re-run the app-shell schematic
  3. Ensure the bootstrap component path resolves to the component you expect (check `main.ts` bootstrapModule/AppComponent)

Example fix

// before
@Component({ selector: 'app-root', template: '<h1>Hello</h1>' })
// after
@Component({ selector: 'app-root', template: '<h1>Hello</h1><router-outlet></router-outlet>' })
Defensive patterns

Strategy: validation

Validate before calling

const template = fs.readFileSync(rootComponentTemplatePath, 'utf8');
if (!/<router-outlet.*?>/.test(template)) {
  throw new Error('Add <router-outlet> to the root component template before running app-shell');
}

Try / catch

try {
  await ngGenerate('app-shell', options);
} catch (e) {
  if (e.message.includes('router-outlet')) {
    console.error('Root component needs <router-outlet> in its template');
  } else throw e;
}

Prevention

When it happens

Trigger: Running `ng add @angular/... app-shell` (or `ng generate app-shell`) on a project whose bootstrap AppComponent template has no `<router-outlet>` element (including self-closing or unclosed forms matched by the regex).

Common situations: Minimal starter apps that render plain content without routing; projects where routing was added to a child component but not the root component; templates built dynamically or split so router-outlet isn't in the root template literal.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/005e78a57ebaaee8. Report an issue: GitHub.