Using the Product Insights Panel in React
Last updated: June 10, 2026
The displays are standard custom elements, so in React you render them like any other element and pass the same attributes documented in the Embed reference. This works in any React setup. We recommend React 19 or later, which passes attributes to custom elements cleanly with no extra wiring.
Load the script once
Include each display's script a single time for the whole app, not inside the component that renders the element. In a Vite or Create React App project, add it to index.html:
<script async src="https://app.lightlabs.com/assets/ll-pip-widget-v5.js"></script>In Next.js (App Router), load it in your root layout with next/script:
import Script from "next/script"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script src="https://app.lightlabs.com/assets/ll-pip-widget-v5.js" strategy="afterInteractive" />
</body>
</html>
)
}Render a panel
function ProductPage() {
return (
<div>
{/** Page content **/}
<lightlabs-widget company-id="YOUR_COMPANY_ID" external-id="your-sku-123" />
</div>
)
}While you build and position the display, use company-id="1" to load example results, then switch to your own company ID before going live.
Switch products with React state
Updating external-id re-renders the display, so you can drive it from state. This is ideal for variant pickers and configurators:
import { useState } from "react"
function VariantPanel({ variants }: { variants: { label: string; externalId: string }[] }) {
const [externalId, setExternalId] = useState(variants[0].externalId)
return (
<>
{variants.map(v => (
<button key={v.externalId} onClick={() => setExternalId(v.externalId)}>
{v.label}
</button>
))}
<lightlabs-widget company-id="YOUR_COMPANY_ID" external-id={externalId} />
</>
)
}Open the report from your own UI
Render the panel with visible="false" so nothing shows on the page, then call openModal() on the element through a ref:
import { useRef } from "react"
function ResultsButton() {
const panel = useRef<HTMLElement & { openModal: () => void }>(null)
return (
<>
<button onClick={() => panel.current?.openModal()}>See lab results</button>
<lightlabs-widget ref={panel} company-id="YOUR_COMPANY_ID" visible="false" />
</>
)
}Using a ref targets that specific element, which is the reliable approach when a page has more than one panel. See Custom triggers and the JavaScript API for the full set of methods.
TypeScript
Declare the custom elements so JSX type-checks. In React 19, augment the react module:
import type { DetailedHTMLProps, HTMLAttributes } from "react"
type LightLabsWidgetProps = DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> & {
"company-id"?: string
"external-id"?: string
"product-id"?: string
visible?: string
debug?: string
}
declare module "react" {
namespace JSX {
interface IntrinsicElements {
"lightlabs-widget": LightLabsWidgetProps
"lightlabs-sticker": LightLabsWidgetProps
}
}
}Notes and gotchas
Pass
visibleas the string"false", not a boolean.visible={false}removes the attribute, which the panel reads as visible. Writevisible="false".Keep the element mounted. Render it once and toggle its behavior through attributes; don't conditionally unmount it just to hide it. Use
visible="false"to hide it while keeping the report available.external-idis the attribute that re-renders. Changingcompany-idafter mount has no effect, so set it once.Strict Mode is fine. The display initializes once even with React's double-invoked mounts in development.