React SDK
@launchly/react — headless hooks and components to embed your changelog in any React app.
@launchly/react brings the data; you bring the markup and styling. It works in
Server Components, SSG/ISR, and the client. Under the hood it calls the public
widget endpoint, so no API key is needed.
Install
npm install @launchly/reactPeer dependency: React ≥ 18.
Exports
fetchLaunchlyChangelog(slug, options?)
Server-side fetch (SSR / SSG / ISR / Server Components).
function fetchLaunchlyChangelog(
slug: string,
options?: { apiBase?: string; fetchOptions?: RequestInit }
): Promise<LaunchlyData>useLaunchlyChangelog(slug, options?)
Client hook.
function useLaunchlyChangelog(
slug: string,
options?: { apiBase?: string }
): LaunchlyState // { project, entries, loading, error }<LaunchlyChangelog>
Headless render-prop component.
<LaunchlyChangelog slug="your-slug">
{({ entries, loading, error }) => /* your UI */}
</LaunchlyChangelog>apiBase defaults to https://launchly.dev everywhere — pass it to target a
self-hosted instance.
Types
type LaunchlyEntry = {
id: string;
title: string;
slug: string;
category: string; // feature | fix | improvement | announcement
version: string | null;
html: string; // sanitized server-side
publishedAt: string | null;
};
type LaunchlyProject = { name: string; slug: string; settings: Record<string, unknown> };
type LaunchlyData = { project: LaunchlyProject; entries: LaunchlyEntry[] };
type LaunchlyState = { project: LaunchlyProject | null; entries: LaunchlyEntry[]; loading: boolean; error: Error | null };SSR / ISR (recommended)
import { fetchLaunchlyChangelog } from "@launchly/react";
export default async function ChangelogPage() {
const { project, entries } = await fetchLaunchlyChangelog("your-slug", {
fetchOptions: { next: { revalidate: 300 } }, // Next.js ISR
});
return (
<main>
<h1>{project.name} changelog</h1>
{entries.map((e) => (
<article key={e.id}>
<h2>{e.title} {e.version && <small>{e.version}</small>}</h2>
<time>{e.publishedAt}</time>
<div dangerouslySetInnerHTML={{ __html: e.html }} />
</article>
))}
</main>
);
}Client hook
"use client";
import { useLaunchlyChangelog } from "@launchly/react";
export function Changelog() {
const { entries, loading, error } = useLaunchlyChangelog("your-slug");
if (loading) return <p>Loading…</p>;
if (error) return <p>Couldn't load updates.</p>;
return <ul>{entries.map((e) => <li key={e.id}>{e.title}</li>)}</ul>;
}Rendering the body
Each entry's html is sanitized server-side by Launchly (scripts, event
handlers, and javascript: URLs are stripped). Render it with
dangerouslySetInnerHTML. For defense-in-depth you can re-sanitize with
DOMPurify, or skip html entirely and use
the structured fields (title, version, category, publishedAt).