Skip to main content

Angular installation

Angular App Testimonial Widget Embed

Angular will not run a script tag you write in a template, by design. So the StoryProof embed becomes a small standalone component: the template binds the widget id onto an empty div, and afterNextRender adds the script once, in the browser, after that div exists.

  • A standalone component with your widget id
  • Script loading that skips the server render
  • How to handle router navigation

Code generator

The component, with your widget id

Enter the widget id and copy both files. The component works in apps with or without server side rendering, because the script is added only in the browser.

Shown in StoryProof when you copy a widget’s embed code.

Part 1 · The standalone component

src/app/storyproof-widget.component.ts

// src/app/storyproof-widget.component.ts (needs signal inputs and afterNextRender)
import { Component, afterNextRender, inject, input } from '@angular/core';
import { DOCUMENT } from '@angular/common';

const SRC = 'https://app.storyproof.io/embed.js';

@Component({
  selector: 'app-storyproof-widget',
  standalone: true,
  // attr. binding writes a real data attribute; leave the div empty.
  template: `<div [attr.data-storyproof-widget]="widgetId()"></div>`,
})
export class StoryProofWidgetComponent {
  widgetId = input.required<string>();
  private readonly doc = inject(DOCUMENT);

  constructor() {
    // Runs in the browser only, after the div is in the DOM.
    afterNextRender(() => {
      if (this.doc.querySelector(`script[src="${SRC}"]`)) return;
      const script = this.doc.createElement('script');
      script.src = SRC;
      script.async = true;
      this.doc.body.appendChild(script);
    });
  }
}

Part 2 · Using it in a template

any component template

<!-- any template; add StoryProofWidgetComponent to that component's imports -->
<section class="testimonials">
  <h2>What customers say</h2>
  <app-storyproof-widget widgetId="YOUR_WIDGET_ID" />
</section>

<!-- Linking to a page with a widget from elsewhere in the app?
     A plain href (no routerLink) loads the page fresh. -->
<a href="/customers">Customer stories</a>

Run ng serve, open the route directly in the browser, and check the console for errors before shipping a production build.

The code is generated in your browser; nothing is sent.

The problem, solved

Angular and third-party embeds: the usual failures and the fix

  • Problem 1

    My pasted script tag does nothing

    Angular’s template guide states it does not support the script element in templates, so the vendor snippet loses its most important line.

  • Problem 2

    Binding the id throws an error

    Angular template bindings target DOM properties by default, and a data attribute has no property behind it.

  • Problem 3

    Server rendering crashes on document

    Touching the browser document during a server render fails or produces markup the client then fights.

  • Problem 4

    Three widgets load three scripts

    A component reused across a page would otherwise append its script once per instance.

  • Problem 5

    Customer content in a production app

    Rendering unreviewed text from the public inside your app is a risk somebody has to own.

In StoryProof

Add it to your Angular app

Step 1 of 5

Get the widget id

Build the widget in StoryProof with its layout and chosen testimonials. The id is the value of data-storyproof-widget in the embed code.

Before you start

Angular caveats

  • Router navigation

    The script fills the StoryProof divs present when it runs. A widget rendered later by a router navigation may stay empty. Link to widget pages with a plain href so they load fresh, and report it to StoryProof support.

  • Not in index.html

    A script tag in index.html can run before Angular has rendered the div, which is why the component adds it after render instead.

  • No npm package

    There is no StoryProof Angular package; this component is the integration. Widgets show a "Powered by StoryProof" badge until Pro.

  • Older Angular versions

    Signal inputs and afterNextRender arrived in recent releases. On older versions, use an @Input and ngAfterViewInit with a browser platform check instead.

  • Angular docs as of 2026-11-13

    APIs follow angular.dev as read on 2026-11-13. The component has not been run against a live StoryProof widget by a person yet.

FAQ

Questions people ask

Short answers to what usually comes up next.

Can I add embed.js to the scripts array in angular.json?

Angular’s workspace docs describe that option for JavaScript files in your workspace, with paths relative to the project. A hosted script is better added at run time as the component does.

Do I need CUSTOM_ELEMENTS_SCHEMA?

No. The widget is a plain div with a data attribute, which Angular already knows. That schema is for unknown element names.

Can I bypass sanitisation and bind the snippet with innerHTML?

Angular sanitises bound HTML and removes the script element, and its security guide warns against bypassing that. The component avoids the question entirely.

Will the testimonials be in my server rendered HTML?

No. The server renders the empty div and the visitor’s browser fills it after the script loads.

Still have a question? Ask the StoryProof team or book a demo.

Get a widget id for your Angular app

Create a workspace, approve a few testimonials, and drop the id into the component.

Sources

  1. Template syntax, Angular documentation. Checked 2026-11-13.
  2. Binding dynamic text, properties and attributes, Angular documentation. Checked 2026-11-13.
  3. afterNextRender, Angular API reference. Checked 2026-11-13.
  4. Security, Angular documentation. Checked 2026-11-13.
  5. Workspace configuration, Angular documentation. Checked 2026-11-13.

Last updated 2026-11-13.