Skip to main content
BEEQ integrates with Angular through @beeq/angular, the official wrapper around BEEQ web components. It gives you familiar Angular syntax for properties, events, and forms — without needing CUSTOM_ELEMENTS_SCHEMA in your modules or manual element registration. For new Angular applications, we recommend the standalone setup with direct component imports from @beeq/angular/standalone. Keep the NgModule setup only for existing module-based apps.

Angular-friendly outputs

Listen to BEEQ events with Angular bindings such as (bqClick) and (bqChange).

Forms support

Use formControlName, [(ngModel)], or signal-driven state with supported BEEQ form controls.

Standalone-first setup

Import only the components you need per feature. NgModule support remains available for older apps.
Angular wrappers in @beeq/angular are detached from change detection, so BEEQ components do not trigger unnecessary Angular repaints. They work well with ChangeDetectionStrategy.OnPush.

Get started

You can add BEEQ to an existing Angular project in a few steps.
1

Check your project setup

Make sure your project has:
  • Angular 14+ (BEEQ wrapper minimum)
  • Node.js 18+
  • A working Angular app (Angular CLI or equivalent)
For signals and computed(), use Angular 16+. Angular 17+ is recommended for new standalone apps.
2

Install the packages

Install the core package and the Angular wrapper:
npm install @beeq/core @beeq/angular
What each package gives you:
  • @beeq/core includes the web components, styles, icons, and shared types
  • @beeq/angular provides Angular bindings, outputs, and value accessors
You do not need to call defineCustomElements() or add CUSTOM_ELEMENTS_SCHEMA to your modules. The Angular wrapper handles component registration for you.
3

Add the global styles

Load the BEEQ stylesheet once for the whole application.Add the stylesheet to your build options:
angular.json
{
  "projects": {
    "my-app": {
      "architect": {
        "build": {
          "options": {
            "styles": [
              "src/styles.scss",
              "node_modules/@beeq/core/dist/beeq/beeq.css"
            ]
          }
        }
      }
    }
  }
}
or import it in your global stylesheet:
src/styles.css
@import "@beeq/core/dist/beeq/beeq.css";
Keep your application stylesheet loaded after the BEEQ stylesheet if you want your own project styles to override shared defaults.
4

Configure icons

Many BEEQ components use SVG icons. Your app must serve the SVG files and tell BEEQ where to find them.
The full BEEQ icon set has 9,000+ SVG files. Copying everything into your repo can slow local builds. Prefer the CDN option for quick starts, or copy only the icons you use.
The fastest option for development is a <script> tag in index.html with the data-beeq attribute. BEEQ reads this path automatically — you do not need setBasePath() in main.ts.
src/index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>My App</title>
    <base href="/" />
    <script
      data-beeq="https://cdn.jsdelivr.net/npm/@beeq/core/dist/beeq/svg/"
    ></script>
  </head>
  <body>
    <app-root></app-root>
  </body>
</html>
The URL must point to the directory that contains BEEQ SVG files (file names such as user.svg, envelope.svg). You can use a CDN URL, a path your app serves locally, or even a different icon library — just make sure the icon name values you use in templates match the file names at that path.
You can also copy the SVG files to your public folder or assets directory and point BEEQ to that folder.
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "cli": {
    "packageManager": "npm"
  },
  "newProjectRoot": "projects",
  "projects": {
    "beeq-angular": {
      //...
      "architect": {
        "build": {
          "builder": "@angular/build:application",
          "options": {
            // ...
            "assets": [
              {
                "glob": "**/*",
                "input": "node_modules/@beeq/core/dist/beeq/svg",
                "output": "/icons/svg/"
              },
              {
                "glob": "**/*",
                "input": "public"
              }
            ],
            "styles": [ "src/styles.css" ]
            // ...
          }
        }
        // ...
      }
    }
  }
}
When you use setBasePath(), you do not need the data-beeq script tag unless you want a fallback.
You can create a Vite plugin to copy only the SVG files you need. See the “Custom Build Pipeline” section in the Angular documentation for guidance, and refer to the React setup here for inspiration.
5

Import BEEQ components (standalone)

Import the BEEQ components you use from @beeq/angular/standalone and list them in the component imports array:
app.component.ts
import { Component } from "@angular/core";
import { BqButton, BqIcon } from "@beeq/angular/standalone";

@Component({
  selector: "app-root",
  standalone: true,
  imports: [BqButton, BqIcon],
  template: `
    <bq-button (bqClick)="save()">
      Save changes
      <bq-icon name="floppy-disk" slot="suffix"></bq-icon>
    </bq-button>
  `,
})
export class AppComponent {
  save(): void {
    console.log("Saved!");
  }
}
Every component you use in the template must appear in the imports array — including child components like bq-icon inside a bq-button.When using form controls with ngModel or reactive forms, also import FormsModule or ReactiveFormsModule and the corresponding value accessor. See the Template-driven forms section for details.

Angular usage patterns with BEEQ

Properties and events

BEEQ components work as custom elements in Angular templates:
<bq-button [disabled]="isSaving" (bqClick)="save()">
  Save changes
</bq-button>
BEEQ eventAngular bindingWhen it fires
bqClick(bqClick)User activates the control
bqChange(bqChange)Value confirmed (blur or selection)
bqInput(bqInput)Value changes on each keystroke
bqFocus(bqFocus)Control receives focus
bqBlur(bqBlur)Control loses focus
bqClear(bqClear)User clears the value
For non-form components such as bq-slider, read the value from the event: (bqChange)="onSliderChange($event)" and use $event.detail.value.

Value accessors for forms

BEEQ components are custom elements, so Angular needs a small adapter to sync [(ngModel)] and formControlName with bqChange / bqInput. BEEQ ships those adapters as value accessors in @beeq/angular/standalone. In standalone components, every dependency must be explicitly imported.
  • Import FormsModule or ReactiveFormsModule
  • Add ngDefaultControl on each bound bq-input (and similar text controls)
  • Import the matching value accessor — for example, BooleanValueAccessor for bq-checkbox
The example below shows a checkbox bound to a signal using [ngModel] for the initial value and (ngModelChange) to update the signal when the user interacts:
import { Component, signal } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { BooleanValueAccessor, BqCheckbox } from "@beeq/angular/standalone";

@Component({
  standalone: true,
  imports: [FormsModule, BooleanValueAccessor, BqCheckbox],
  template: `
    <bq-checkbox
      [ngModel]="isChecked()"
      (ngModelChange)="isChecked.set($event)"
    >
      <span slot="label">Checkbox</span>
    </bq-checkbox>
  `,
})
export class AppComponent {
  isChecked = signal(false);
}
Value Accessor reference:
ComponentValue AccessorNotes
bq-checkbox, bq-switchBooleanValueAccessorReads/writes checked (boolean)
bq-input (text)Use ngDefaultControl to opt into Angular’s built-in DefaultValueAccessor
bq-input[type=“number”]NumericValueAccessorReads/writes value as a number
bq-textareaTextValueAccessorReads/writes value as a string
bq-date-pickerTextValueAccessorReads/writes value as a string
bq-selectSelectValueAccessorReads/writes value (single or multiple)
bq-radioRadioValueAccessorReads/writes value within a radio group

Reactive forms

Use ReactiveFormsModule, a FormGroup, and formControlName. Add ngDefaultControl on text-based BEEQ inputs so Angular treats them as ControlValueAccessor. For boolean controls like bq-checkbox, import BooleanValueAccessor instead.
import { Component } from "@angular/core";
import {
  FormControl,
  FormGroup,
  ReactiveFormsModule,
  Validators,
} from "@angular/forms";
import {
  BooleanValueAccessor,
  BqButton,
  BqCard,
  BqCheckbox,
  BqIcon,
  BqInput,
} from "@beeq/angular/standalone";

@Component({
  selector: "app-profile-form",
  standalone: true,
  imports: [
    ReactiveFormsModule,
    BooleanValueAccessor,
    BqCard,
    BqInput,
    BqIcon,
    BqCheckbox,
    BqButton,
  ],
  template: `
    <bq-card>
      <h2>Reactive form</h2>
      <form [formGroup]="userProfile" (ngSubmit)="onSubmit()">
        <bq-input
          name="name"
          formControlName="name"
          placeholder="Your name"
          ngDefaultControl
        >
          <span slot="label">Name</span>
          <bq-icon name="user" slot="prefix"></bq-icon>
        </bq-input>

        <bq-input
          required
          name="email"
          formControlName="email"
          placeholder="Your email"
          ngDefaultControl
        >
          <span slot="label">Email</span>
          <bq-icon name="envelope" slot="prefix"></bq-icon>
        </bq-input>

        <bq-checkbox
          name="userTermsConditions"
          formControlName="userTermsConditions"
        >
          Yes, I agree with the Terms &amp; Conditions
        </bq-checkbox>

        <bq-button type="submit">Subscribe</bq-button>
      </form>
    </bq-card>
  `,
})
export class ProfileFormComponent {
  userProfile = new FormGroup({
    name: new FormControl(""),
    email: new FormControl("", Validators.required),
    userTermsConditions: new FormControl(false),
  });

  onSubmit(): void {
    console.log(this.userProfile.value);
  }
}
ngDefaultControl tells Angular that the web component implements ControlValueAccessor. Add it on text-based inputs (bq-input, bq-textarea). Do not add it on bq-checkbox or bq-switch when BooleanValueAccessor is already imported — that accessor takes precedence and ngDefaultControl would conflict.

Template-driven forms

When using template-driven forms, import FormsModule and bind values with [(ngModel)].
import { Component } from "@angular/core";
import { FormsModule } from "@angular/forms";
import {
  BooleanValueAccessor,
  BqButton,
  BqCard,
  BqCheckbox,
  BqIcon,
  BqInput,
} from "@beeq/angular/standalone";

@Component({
  selector: "app-newsletter",
  standalone: true,
  imports: [
    FormsModule,
    BooleanValueAccessor,
    BqCard,
    BqInput,
    BqIcon,
    BqCheckbox,
    BqButton,
  ],
  template: `
    <bq-card>
      <h2>Template-driven form</h2>
      <form #form="ngForm" (ngSubmit)="onSubmit(form)">
        <bq-input
          name="name"
          [(ngModel)]="username"
          placeholder="Your name"
          ngDefaultControl
        >
          <span slot="label">Name</span>
          <bq-icon name="user" slot="prefix"></bq-icon>
        </bq-input>

        <bq-input
          required
          name="email"
          [(ngModel)]="email"
          placeholder="Your email"
          ngDefaultControl
        >
          <span slot="label">Email</span>
          <bq-icon name="envelope" slot="prefix"></bq-icon>
        </bq-input>

        <bq-checkbox
          name="userTermsConditions"
          [(ngModel)]="termsConditions"
        >
          Yes, I agree with the Terms &amp; Conditions
        </bq-checkbox>

        <bq-button type="submit">Subscribe</bq-button>
      </form>
    </bq-card>
  `,
})
export class NewsletterComponent {
  username = "";
  email = "";
  termsConditions = false;

  onSubmit(form: { value: unknown }): void {
    console.log(form.value);
  }
}

Signals

Use signals for local UI state and computed() for derived labels or styles. For bq-slider, bind the value with [value] and update the signal in (bqChange) — do not use [(ngModel)] on a WritableSignal.
import { Component, computed, signal } from "@angular/core";
import { BqCard, BqSlider, BqTag } from "@beeq/angular/standalone";

@Component({
  selector: "app-skill-level",
  standalone: true,
  imports: [BqCard, BqSlider, BqTag],
  template: `
    <bq-card>
      <h2>
        Skill level:
        <bq-tag [color]="skillLevelColor()" size="small">
          {{ skillLevelLabel() }}
        </bq-tag>
      </h2>
      <bq-slider
        [value]="skillLevel()"
        [enableTooltip]="true"
        [tooltipAlwaysVisible]="true"
        (bqChange)="skillLevel.set(+$event.detail.value)"
      ></bq-slider>
    </bq-card>
  `,
})
export class SkillLevelComponent {
  skillLevel = signal(50);

  skillLevelLabel = computed(() => {
    const value = this.skillLevel();
    if (value < 34) return "Beginner";
    if (value < 67) return "Intermediate";
    return "Expert";
  });

  skillLevelColor = computed(() => {
    const value = this.skillLevel();
    if (value < 34) return "gray";
    if (value < 67) return "warning";
    return "success";
  });
}
You can combine reactive forms with signals using toSignal() from @angular/core/rxjs-interop (Angular 16+) to mirror FormControl value changes in the template.

Example application

A reference standalone app is available in the BEEQ repository and demonstrates all of the patterns described on this page:
  • Icons via data-beeq in index.html
  • Global styles via @import in styles.css
  • Reactive form, template-driven form, and signal-driven slider in one App component

Angular Module integration (Legacy)

If your application still uses NgModule, import BeeQModule.forRoot() in your root module.
Value accessor directives are included automatically when you import BeeQModule. You do not need to import them individually. However, FormsModule and ReactiveFormsModule must still be imported separately for ngModel and reactive forms to work.
app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { BeeQModule } from "@beeq/angular";

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

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, FormsModule, BeeQModule.forRoot()],
  bootstrap: [AppComponent],
})
export class AppModule {}

Troubleshooting

Confirm @beeq/core/dist/beeq/beeq.css is loaded once globally (angular.json or your main stylesheet).
Check one of these setups:
  • data-beeq in index.html points to a URL that serves BEEQ SVG files (correct file names)
  • setBasePath() in main.ts matches the public path where your build serves copied SVGs
  • Icon name values match SVG file names (without .svg)
In standalone apps, add each Bq* component to the imports array of the declaring component. In NgModule apps, import BeeQModule.forRoot().
Import FormsModule or ReactiveFormsModule, add ngDefaultControl on BEEQ text inputs, and import BooleanValueAccessor for bq-checkbox and bq-switch in standalone apps.
Some Angular 14–15 builds hit compiler issues with web-component value accessors. If you see NG8001 or accessor-related errors:
  1. Add "skipLibCheck": true to tsconfig.json compilerOptions.
  2. In angular.json, set "aot": false and "buildOptimizer": false for affected configurations.
On Angular 16+ with current BEEQ releases, these workarounds are usually unnecessary.

Next steps

Explore components

Start with a component page to see props, events, slots, and framework examples.

Customize the theme

Review theming options if your product needs brand customization.

Open Storybook

Explore component states and interactions in the live component library.