Skip to Content

Feedback

Collect feedback on pages and selected text.

Readers rate a page from a form below it, or comment on text they select. You decide where the messages go.

Setup

Install

npm i @fumapress/feedback

Add the Styles

Add this after the imports in your stylesheet:

src/app.css
@import "@fumapress/feedback/css/preset.css";

Choose Where Feedback Goes

Use one of these options. They all register the same routes, so a site can only have one.

Plain functions run on the server, so the site needs a server.

press.config.tsx
import { feedbackPlugin } from "@fumapress/feedback";

export default createPress(content).plugins(
  feedbackPlugin({
    async onPageFeedbackAction({ opinion, url, message }) {
      await analytics.track("page-feedback", { opinion, url, message });
      return {};
    },
    async onTextFeedbackAction({ url, blockId, blockBody, message }) {
      const discussion = await createDiscussion({ url, blockId, blockBody, message });
      return { githubUrl: discussion.url };
    },
  }),
);

The returned githubUrl or emailSent tells the form where the feedback went. Types are in @fumapress/feedback/schema.

Rate Limiting

Anyone can post, and the plugins don't limit field lengths or URL schemes. Add rate limiting and bot checks before you go live.

Each plugin takes beforeRequest. Return a Response to reject a request:

press.config.tsx
feedbackPlugin({
  async beforeRequest(req) {
    if (await isRateLimited(req)) return new Response("Too Many Requests", { status: 429 });
  },
});

Custom Layout

The default layout places the form for you.

In a custom layout, place the feedback:page slot in your island:

src/components/docs-island.tsx
export const DocsIsland = createIsland(
  ({ layoutProps, pageData, toc, slots, children }: IslandProps<typeof app, "docs">) => (
    <DocsLayout {...layoutProps}>
      <DocsPage toc={toc}>
        <DocsTitle>{pageData.title}</DocsTitle>
        <DocsBody>{children}</DocsBody>
        {slots["feedback:page"]?.element}
      </DocsPage>
    </DocsLayout>
  ),
);

Options

Prop

Type

Last updated on

On this page