Encatch
Welcome to Encatch Docs
IntegrationsDocumentation Platforms

Astro Starlight

Add Encatch documentation feedback to an Astro Starlight site using component overrides and React islands

Collect page-level feedback on every Starlight doc page: helpful votes, suggest an edit, and raise an issue. This guide follows the working example in get-encatch/astro-starlight-examples.


Overview

Starlight does not ship a layout hook for arbitrary footer widgets, so this integration overrides two built-in components:

Starlight componentPurpose
FooterRenders the Encatch feedback row above the default footer (pagination, edit link)
PageFrameMounts a small React island that initializes the Encatch Web SDK on each page

The feedback UI is a React island (client:load) so it stays interactive while the rest of the page remains static Astro output.

What you'll need

  • An Astro Starlight project (@astrojs/starlight and @astrojs/react)
  • @encatch/web-sdk 1.4.0 or later
  • A publishable SDK key with your docs origin in Allowed Domains / Packages
  • A published documentation feedback form in Encatch (combined form with logic jumps for helpful / suggest edit / raise issue)

Step 1: Install dependencies

In your Starlight project:

pnpm add @encatch/web-sdk @astrojs/react react react-dom lucide-react
pnpm add -D @types/react @types/react-dom
npm install @encatch/web-sdk @astrojs/react react react-dom lucide-react
npm install -D @types/react @types/react-dom
yarn add @encatch/web-sdk @astrojs/react react react-dom lucide-react
yarn add -D @types/react @types/react-dom

Enable React and register Starlight component overrides in astro.config.mjs:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import react from '@astrojs/react';

export default defineConfig({
  integrations: [
    react(),
    starlight({
      title: 'My docs',
      customCss: ['./src/styles/encatch.css'],
      components: {
        Footer: './src/components/EncatchFooter.astro',
        PageFrame: './src/components/EncatchPageFrame.astro',
      },
    }),
  ],
});

Step 2: Configure environment variables

Copy these variables into .env (use the PUBLIC_ prefix so Astro exposes them to client code):

# Required
PUBLIC_ENCATCH_SDK_PUBLISHABLE_KEY=
PUBLIC_ENCATCH_DOCUMENTATION_FEEDBACK_FORM_SLUG=documentation_feedback
PUBLIC_ENCATCH_FEEDBACK_TYPE_QUESTION_SLUG=documentation_feedback_type
PUBLIC_ENCATCH_PAGE_URL_QUESTION_SLUG=page_url
PUBLIC_ENCATCH_HELPFUL_CHOICE_QUESTION_SLUG=helpful_question_choice

# Optional — leave blank for encatch.com defaults
PUBLIC_ENCATCH_API_HOST=
PUBLIC_ENCATCH_WEB_HOST=
VariableDescription
PUBLIC_ENCATCH_SDK_PUBLISHABLE_KEYPublishable key from Settings → Publishable SDK Keys
PUBLIC_ENCATCH_DOCUMENTATION_FEEDBACK_FORM_SLUGSlug of your combined documentation feedback form
PUBLIC_ENCATCH_FEEDBACK_TYPE_QUESTION_SLUGHidden/routing question that selects helpful vs suggest-edit vs raise-issue
PUBLIC_ENCATCH_PAGE_URL_QUESTION_SLUGQuestion prefilled with the current page URL
PUBLIC_ENCATCH_HELPFUL_CHOICE_QUESTION_SLUGYes/No question prefilled when the reader votes helpful

The example repo ships a one-click Install form button in its README if you need the combined form created in your workspace automatically.


Step 3: Add SDK helpers

Create src/components/encatch.ts to initialize the SDK and open the combined form with the correct logic-jump prefills:

import { _encatch } from '@encatch/web-sdk';
import type { Theme } from '@encatch/web-sdk';

type DocumentationFeedbackRoute = 'page-helpful' | 'suggest-edit' | 'raise-issue';

function ensureEncatchInitialized(options?: { theme?: Theme }): boolean {
  if (typeof window === 'undefined') return false;

  const apiKey = import.meta.env.PUBLIC_ENCATCH_SDK_PUBLISHABLE_KEY?.trim();
  if (!apiKey) {
    console.warn('PUBLIC_ENCATCH_SDK_PUBLISHABLE_KEY is not set');
    return false;
  }

  if (!_encatch._initialized) {
    _encatch.init(apiKey, { theme: options?.theme ?? 'system' });
  }
  return true;
}

export function openHelpfulFeedbackForm(
  pageUrl: string,
  vote: 'yes' | 'no',
  locale?: string,
) {
  // Prefill feedback type, page URL, and yes/no — then showForm(formSlug)
  // See full implementation in get-encatch/astro-starlight-examples
}

export function openSuggestEditForm(pageUrl: string, locale?: string) {
  // Route to suggest-edit branch of the combined form
}

export function openRaiseIssueForm(pageUrl: string, locale?: string) {
  // Route to raise-issue branch of the combined form
}

The example repository contains the full openDocumentationFeedbackForm helper, host overrides, and locale sync.


Step 4: Build the feedback UI

Create src/components/DocsPageFeedback.tsx — a React footer row with helpful votes and action buttons:

import { useState } from 'react';
import { CircleAlert, Pencil, ThumbsDown, ThumbsUp } from 'lucide-react';
import {
  openHelpfulFeedbackForm,
  openRaiseIssueForm,
  openSuggestEditForm,
} from './encatch';

export function DocsPageFeedback({ pageUrl, locale, helpfulQuestion, yes, no, suggestEdits, raiseIssue }) {
  const [vote, setVote] = useState<'yes' | 'no' | null>(null);

  const handleVote = (next: 'yes' | 'no') => {
    const newVote = vote === next ? null : next;
    setVote(newVote);
    if (newVote) openHelpfulFeedbackForm(pageUrl, newVote, locale);
  };

  return (
    <div className="encatch-feedback">
      {/* Helpful question + Yes/No pills */}
      {/* Suggest edits + Raise issue buttons */}
    </div>
  );
}

Copy the complete component from the example repo.


Step 5: Override Starlight components

PageFrame — initialize the SDK

src/components/EncatchPageFrame.astro wraps the default frame and mounts EncatchInit as a client island:

---
import Default from '@astrojs/starlight/components/PageFrame.astro';
import { EncatchInit } from './EncatchInit';

const locale = Astro.currentLocale ?? 'en';
---

<Default>
  <slot />
  <Fragment slot="header"><slot name="header" /></Fragment>
  <Fragment slot="sidebar"><slot name="sidebar" /></Fragment>
</Default>
<EncatchInit client:load locale={locale} />

EncatchInit.tsx calls ensureEncatchInitialized() and syncEncatchLocale(locale) inside a useEffect.

src/components/EncatchFooter.astro renders the feedback row only on documentation pages:

---
import Default from '@astrojs/starlight/components/Footer.astro';
import { DocsPageFeedback } from './DocsPageFeedback';

const { entry, id } = Astro.locals.starlightRoute;
const locale = Astro.currentLocale ?? 'en';
const isDocPage = id !== undefined;
---

{isDocPage && (
  <DocsPageFeedback
    client:load
    pageUrl={Astro.url.pathname}
    pageTitle={entry.data.title}
    locale={locale}
    helpfulQuestion={Astro.locals.t('docsFeedback.helpfulQuestion')}
    yes={Astro.locals.t('docsFeedback.yes')}
    no={Astro.locals.t('docsFeedback.no')}
    suggestEdits={Astro.locals.t('docsFeedback.suggestEdits')}
    raiseIssue={Astro.locals.t('docsFeedback.raiseIssue')}
  />
)}
<Default><slot /></Default>

Add docsFeedback.* strings to your Starlight i18n files under src/content/i18n/ (or hard-code labels while prototyping).


Add src/styles/encatch.css and reference it in starlight({ customCss: [...] }). Use Starlight CSS variables (--sl-color-text, --sl-color-gray-5, etc.) so the footer matches light and dark themes.

See encatch.css in the example repo for pill buttons, spacing, and border treatment.


Step 7: Run and verify

pnpm dev

Open a doc page, click Yes or No, Suggest edits, or Raise issue, and confirm the Encatch modal opens with the page URL prefilled. Check responses in your Encatch dashboard under the linked form.

Domain allowlist

If the modal does not open, confirm your local or production docs URL is listed on the publishable key's Allowed Domains / Packages (for example localhost:4321 during development).


Next steps

  • Route feedback to your team — Connect GitHub, Slack, or other destinations
  • Customize the form — Edit questions and logic jumps in the Encatch dashboard
  • Web SDK reference — Advanced APIs such as addToResponse, locale, and theme: JavaScript Web SDK

Was this page helpful?