Docs

React and Next.js

The widget is framework-free, so using it in React is just: load the script once, render a div.

A drop-in component

This client component loads widget.js on mount and cleans up after itself on unmount. Replace your-wall-slug with the slug from your Share page.

testimonial-wall.tsx
"use client";

import { useEffect } from "react";

const WIDGET_SRC = "https://laudiance.com/widget.js";

export function TestimonialWall() {
  useEffect(() => {
    // Append the script once. If another instance of this component
    // (or your layout) already added it, do nothing.
    if (document.querySelector(`script[src="${WIDGET_SRC}"]`)) return;

    const script = document.createElement("script");
    script.src = WIDGET_SRC;
    script.async = true;
    document.body.appendChild(script);

    return () => {
      // Remove only the script we added.
      script.remove();
    };
  }, []);

  return <div data-laudiance-wall="your-wall-slug" data-layout="grid" />;
}

A note on Strict Mode

In development, React Strict Mode mounts components twice, so the effect runs, cleans up, and runs again. That is fine here for two reasons: the querySelector guard prevents a second script tag, and the widget itself marks every element it has already rendered, so a re-run never duplicates content. Navigating away and back in a single-page app is also safe: the widget watches the DOM with a MutationObserver and picks up new data-laudiance-wall divs automatically.

If the script is already on the page

If you load widget.js once in your root layout or HTML template, components only need to render the marker div. No effect, no cleanup:

Anywhere in JSX
{/* Anywhere in your JSX, if widget.js is already loaded globally: */}
<div data-laudiance-wall="your-wall-slug" data-layout="carousel" data-theme="auto" />

Styling

The widget renders inside Shadow DOM and fills the width of its container, with automatic height. Style the wrapper you place it in; the inside styles itself and will not fight your CSS. Layout, card style, theme, accent, corner radius and language are all controlled with the data attributes described in the quickstart, and you can drive the whole palette from your own stylesheet with the --laudiance-* custom properties. Those cross the Shadow DOM boundary on purpose; nothing else does.