Launchly Docs

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/react

Peer 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 };
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).

On this page