@frillco/script
v0.1.3
Published
Type-safe SDK for embedding Frill widgets, surveys and announcements.
Maintainers
Readme
@frillco/script
Type-safe SDK for embedding Frill widgets, surveys and boosted announcements.
Full documentation: developers.frill.co
Install
npm install @frillco/scriptUsage
Start by loading your container — it creates the widgets, surveys and boosted announcements you've configured in your Frill dashboard, and is the recommended entry point for most apps.
frill is the typed callable (a drop-in for window.Frill). Calling it loads the Frill script on
first use, so calling frill('container', …) up front also loads Frill eagerly.
import { frill } from '@frillco/script';
// Load your container (optionally identifying the current user)
await frill('container', {
key: 'YOUR_CONTAINER_KEY',
user: { name: 'Mikey Hill', email: '[email protected]' },
});
// Open a specific widget on demand
const widget = await frill('widget', { key: 'YOUR_WIDGET_KEY' });
widget.open();Loading eagerly
To start loading the Frill script before you make your first call, use loadFrill (similar to
loadStripe). Pass domain: 'frill.eu' for EU data residency.
import { loadFrill } from '@frillco/script';
loadFrill(); // begins loading immediately
// loadFrill({ domain: 'frill.eu' });Identifying users
// Guest user
await frill('identify', {
name: 'Mikey Hill',
email: '[email protected]',
attributes: { mrr: 100 },
});
// SSO (JWT from your server)
await frill('identify', { ssoToken: 'SSO_JWT' });
// Log out
await frill('unidentify');Controlling a widget
const widget = await frill('widget', { key: 'YOUR_WIDGET_KEY' });
widget.open();
widget.viewSection('roadmap');
widget.events.on('badgeCount', ({ count }) => console.log(`${count} unread`));Surveys
const survey = await frill('survey', { key: 'YOUR_SURVEY_KEY', force: true });
survey.open();React / Next.js
Render <FrillScript /> once (e.g. in your root layout) to load the container before hydration.
import Script from 'next/script';
import { FrillScript } from '@frillco/script/react';
// app/layout.tsx
<FrillScript
as={Script}
strategy="beforeInteractive"
container={{
key: 'YOUR_CONTAINER_KEY',
user: { name: 'Mikey Hill', email: '[email protected]' },
}}
/>;Content-Security-Policy (nonce)
Running a strict CSP? Pass a nonce. It's applied to the <FrillScript> tag, the container
script it injects, and every script/style tag the SDK injects thereafter — so Frill loads
without 'unsafe-inline'.
<FrillScript nonce={nonce} container={{ key: 'YOUR_CONTAINER_KEY' }} />loadFrill({ nonce }) does the same for the imperative API.
Hooks
useWidget returns the widget instance (or null until it's ready), re-rendering when it loads —
so effects can reliably subscribe via widget.events.
import { useWidget } from '@frillco/script/react';
function FeedbackButton() {
const widget = useWidget({ key: 'YOUR_WIDGET_KEY' });
React.useEffect(() => {
if (!widget) return;
const unsubscribe = widget.events.on('badgeCount', ({ count }) => {
console.log(`${count} unread`);
});
return () => {
unsubscribe();
};
}, [widget]);
return <button onClick={() => widget?.open()}>Feedback</button>;
}useSurvey and useContainer mirror useWidget (for containers, prefer
<FrillScript container={…} /> when you can). useFrill() returns the frill callable for
imperative use in event handlers.
Embedding a widget
Render a widget inline (into a <div>, not a modal launcher):
import { FrillWidgetEmbed } from '@frillco/script/react';
<FrillWidgetEmbed widgetKey="YOUR_WIDGET_KEY" style={{ width: 340, height: 460 }} />;