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/reactUsing 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.
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.
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
}| Code | What happened |
|---|---|
invalid_key | The API key is wrong or turned off. |
validation_failed | Something in the form is missing or too long. |
rate_limited | This device hit its daily limit. |
limit_reached | Your plan's limit was reached. |
not_found | That request no longer exists. |
network | No connection, or the server didn't answer. |
server | Something 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.