Fewchurs
Docs / React

React

Add a feature request board to any React app: Vite, Create React App, Remix or your own setup.

Needs React 18 or later.

Install

npm install @fewchurs/react

Using Next.js? Follow the Next.js guide instead. It keeps your key on the server.

Show the board

Wrap the board in FewchursProvider and give it your API key. Import the styles file once.

App.tsx
import { FewchursBoard, FewchursProvider } from "@fewchurs/react";
import "@fewchurs/react/styles.css";

export function App() {
  return (
    <FewchursProvider apiKey={import.meta.env.VITE_FEWCHURS_KEY}>
      <FewchursBoard />
    </FewchursProvider>
  );
}

Keeping the key private

In an app with no server, the key ends up in your JavaScript files. That is okay: it only works for your board, and each device has daily limits.

If you have a server and want to hide the key, send the board's calls to your own server instead. Your server adds the key and forwards the call.

// Your server adds the API key. The browser never sees it.
<FewchursProvider baseUrl="/api/fewchurs">
  <FewchursBoard />
</FewchursProvider>

Change the look

Set colors and text with props. You can also set CSS variables like --fw-primary and --fw-radius. Light and dark mode follow the visitor's system setting.

<FewchursBoard
  theme={{ primary: "#0f766e", radius: 8 }}
  labels={{ title: "Ideas", submit: "Suggest an idea" }}
/>

Want to draw each row yourself? Use a render function, or build the board from parts:

<FewchursBoard
  renderFeature={(feature, actions) => (
    <MyRow feature={feature} onVote={actions.toggleVote} />
  )}
/>

// Or put the parts together yourself
<FewchursBoard.Root unstyled>
  <FewchursBoard.Tabs />
  <FewchursBoard.List />
  <FewchursBoard.SubmitTrigger />
</FewchursBoard.Root>

unstyled removes all our styles but keeps the behavior and keyboard support.

Open it in a dialog

FewchursBoardDialog puts the whole board in a modal, so a "Feedback" button anywhere in your app can open it. It takes every FewchursBoard prop, plus open and onClose. Escape closes it, and focus returns to the button that opened it.

FeedbackButton.tsx
import { useState } from "react";
import { FewchursBoardDialog, FewchursProvider } from "@fewchurs/react";
import "@fewchurs/react/styles.css";

export function FeedbackButton() {
  const [open, setOpen] = useState(false);

  return (
    <FewchursProvider apiKey={import.meta.env.VITE_FEWCHURS_KEY}>
      <button onClick={() => setOpen(true)}>Feedback</button>
      <FewchursBoardDialog open={open} onClose={() => setOpen(false)} />
    </FewchursProvider>
  );
}

Link to one request

initialFeatureId shows one request above the list, with its comments already open. Use it for a link from an email or a changelog straight to the request someone voted on.

// e.g. /feedback?request=abc123
const requestId = new URLSearchParams(location.search).get("request");

<FewchursBoard initialFeatureId={requestId ?? undefined} />

It works the same on the Next.js board, where the request is rendered on the server. If the id no longer exists, the board just shows the list. To fetch one request without the board, use useFeature:

const { feature, isLoading, error, refresh } = useFeature(requestId);

Build your own UI

The hooks give you the data and actions with no markup. Use them inside FewchursProvider. They share one store, so a vote updates everywhere at once.

const { features, isLoading, loadMore, hasMore } = useFeatures({ sort: "top" });
const { hasVoted, upvoteCount, toggle } = useVote(featureId);
const { comments, add } = useComments(featureId);
const { submit, isPending } = useSubmitFeature();
const { isFollowing, follow, unfollow } = useFollow(featureId);
const { feature } = useFeature(featureId);

Handling errors

Nothing throws. When a call fails, you get an error with a code. userMessage(error) turns it into a short sentence you can show.

import { useSubmitFeature } from "@fewchurs/react";
import { userMessage } from "@fewchurs/react/core";

const { submit, error } = useSubmitFeature();

const feature = await submit({ title, description });
if (!feature && error) {
  showToast(userMessage(error)); // a friendly sentence for your users
}
CodeWhat happened
invalid_keyThe API key is wrong or turned off.
validation_failedSomething in the form is missing or too long.
rate_limitedThis device hit its daily limit.
limit_reachedYour plan's limit was reached.
not_foundThat request no longer exists.
networkNo connection, or the server didn't answer.
serverSomething broke on our side. Try again.

Signed-in users

By default each visitor gets a random ID, saved in the browser. If your app has accounts, pass a hashed user ID. Then votes follow the person to every device.

// Use a hash of your user's ID, never the raw ID or an email
<FewchursProvider apiKey={key} deviceId={hashedUserId}>

The "Powered by" badge

The free plan shows a small "Powered by Fewchurs" line. It goes away on its own when you upgrade to Starter or Pro.

Stuck? A board that loads but stays empty usually means the API key is missing. Check that the environment variable is set where your app is built.