@fewchurs/next
A server-rendered feature-request board for the Next.js App Router: visitors submit ideas, vote, comment and follow, and you manage everything from your Fewchurs dashboard.
Your API key stays on the server. The board renders with real data on first paint, and voting and commenting go through a route handler in your own app, so the key never reaches the browser and no CORS setup is needed.
Not using Next.js? Use @fewchurs/react.
- Next.js 14+ (App Router), React 18 or 19, Node 20+ (the handler also runs on the edge runtime)
- Light and dark mode out of the box
- Fully typed
Integrate in 5 steps
1. Get an API key and add your domain
In the Fewchurs dashboard, open your app, copy the API key (it looks like
fr_live_…) and add the domain your site runs on. localhost always works while you build.
2. Install
npm install @fewchurs/next
# or: pnpm add @fewchurs/next
# or: yarn add @fewchurs/next
3. Add the key to .env.local
FEWCHURS_API_KEY=fr_live_…
Do not prefix it with NEXT_PUBLIC_. It is a server-only secret here. Restart next dev after you
edit the file.
4. Create the route handler
This is the small proxy the board talks to. It adds your key on the server.
// app/api/fewchurs/[...route]/route.ts
export { GET, POST, DELETE } from "@fewchurs/next/handler";
Keep the folder name [...route] exactly as written.
5. Render the board
// app/feedback/page.tsx
import { FewchursBoard } from "@fewchurs/next";
import "@fewchurs/next/styles.css";
export default function Page() {
return <FewchursBoard />;
}
Open /feedback. You should see the board with its tabs, voting, comments and a New request form.
Or add a feedback button to every page
FewchursButton renders only a button. Clicking it slides the board in as a sheet, and nothing is
fetched until it opens. Put it in your root layout so it shows everywhere. It does not make your pages
dynamic.
// app/layout.tsx
import { FewchursButton } from "@fewchurs/next";
import "@fewchurs/next/styles.css";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<FewchursButton floating>Suggest a feature</FewchursButton>
</body>
</html>
);
}
floating pins it to the bottom right, floating="left" to the bottom left. Leave the children out
for the default "Feedback" label.
Deploying and preview URLs
The package reads your domain from each request and checks it against the domains you added in the dashboard. When your site runs on another address, such as a preview deployment, set the domain yourself:
FEWCHURS_APP_ID=yourcompany.com
Setting it also lets you test your production domain from localhost. Add FEWCHURS_API_KEY to your
hosting provider's environment variables too.
Customize
CSS variables:
.fewchurs {
--fw-primary: #ed5f18;
--fw-radius: 12px;
--fw-font: var(--font-sans);
}
Props:
<FewchursBoard
tabs={["top", "new", "roadmap"]} // default: ["top", "new"]
defaultTab="top"
showDeclined={false}
allowComments
emailField="optional"
pageSize={20}
theme={{ primary: "#0f766e", radius: 4 }}
labels={{ title: "Ideas", submit: "Suggest an idea" }}
/>
<FewchursBoard> is a server component, so it cannot take functions (renderHeader, renderEmpty,
renderFeature, onEvent). When you need those, wrap the client board from @fewchurs/react in
your own "use client" file, with <FewchursProvider baseUrl="/api/fewchurs"> around it.
Link straight to one request
// app/feedback/page.tsx
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ request?: string }>;
}) {
const { request } = await searchParams;
return <FewchursBoard initialFeatureId={request} />;
}
Visit /feedback?request=<id> and the request shows above the list with its comments open, rendered on
the server. On Next 14, searchParams is a plain object instead of a Promise.
Signed-in users
Votes are anonymous and tied to an httpOnly cookie that the route handler sets on the first
request. To make votes follow the account across devices, return a hashed user id from both the
handler and the board. Never use a raw id or an email address.
// app/api/fewchurs/[...route]/route.ts
import { createHandler } from "@fewchurs/next/handler";
export const { GET, POST, DELETE } = createHandler({
deviceId: async () => hash((await auth()).userId),
});
<FewchursBoard deviceId={async () => hash((await auth()).userId)} />
Options
| Option | Where it is read | Default |
|---|---|---|
apiKey | FEWCHURS_API_KEY | none, required |
appId | FEWCHURS_APP_ID | the host of the request |
apiPath | FEWCHURS_API_PATH | /api/fewchurs |
baseUrl | FEWCHURS_BASE_URL | the Fewchurs API |
Pass them as props to <FewchursBoard> and createHandler({ … }), or set the environment variable.
If you mount the handler somewhere other than /api/fewchurs, set apiPath on both.
Troubleshooting
| You see | Why, and what to do |
|---|---|
| "Fewchurs isn't set up" | FEWCHURS_API_KEY is missing. Add it to .env.local and restart next dev. |
| "Invalid API key" (401) | The key is wrong. Copy it again from the dashboard. |
| "Invalid API key" (403) | The domain is not added to your app. The message names the domain to add. |
| Votes and comments fail with a 404 | The route handler is missing, or not at app/api/fewchurs/[...route]/route.ts. |
| The board has no styling | You forgot import "@fewchurs/next/styles.css". |
Build error about server-only | <FewchursBoard> was imported into a "use client" file. Render it from a server component. This is deliberate, so a key is never bundled for the browser. |
Other entry points
@fewchurs/next/actions:submitFeatureAction,voteAction,commentAction,followAction, for boards built from plain forms.@fewchurs/next/identity: thehttpOnlydevice-id cookie helpers.@fewchurs/next/handler:createHandler(options)and the ready-madeGET,POST,DELETE.
More
- Full guide and a working example app: github.com/fewchurs/fewchurs-react
- Dashboard and docs: www.fewchurs.com
- Changelog: CHANGELOG.md
License
Proprietary. You may use the package in your own apps to connect them to Fewchurs and customize it through the documented props, CSS variables, slots and hooks. See LICENSE.