Framework guide
Feature-request board for Remix
Feedback is a React client component. Mount it from a Remix client route or a client-only island. Pass user from your session loader, Feedback does not authenticate board end users. Scope is embed-only; there is no public board URL.
Install
pnpm add @bootstrapware/feedback
Import @bootstrapware/feedback/styles.css from the client module that renders the board (or from your root stylesheet if you prefer one global import).
Client island
import { Feedback, createLocalAdapter } from "@bootstrapware/feedback";
import "@bootstrapware/feedback/styles.css";
export function FeedbackBoard({ user }: { user: { id: string; name?: string } }) {
return (
<Feedback
user={user}
adapter={createLocalAdapter({ storageKey: "remix-feedback" })}
/>
);
}Mount <FeedbackBoard /> from a route that is already behind your session. Remix loaders stay for page data; BYO writes go through resource routes.
Loader → user prop
// app/routes/settings.ideas.tsx
export async function loader({ request }: LoaderFunctionArgs) {
const session = await requireUserSession(request);
return json({
user: { id: session.userId, name: session.displayName },
});
}Pass that user into the client island. Opaque stable ids matter for voting, see identity.
BYO resource routes
Pass a FeedbackAdapter that hits your Remix resource routes. Authenticate the session cookie, re-bind authorship, enforce title ≤ 200 / body ≤ 5000, and store statuses as open | planned | in_progress | shipped | declined.
// Illustrative, your routes, your schema
export async function action({ request }: ActionFunctionArgs) {
const session = await requireUserSession(request);
const body = await request.json();
// Create post / vote using session.userId, not body.author.id alone.
return json({ ok: true });
}Hosted configuration and env
Expose only publishable keys to the browser. Pattern depends on your Remix setup (classic window.ENV injection from the root loader, or Vite-based Remix env). Never ship bsw_*_sec_ to the client. Details: API keys.
<Feedback
boardId={ENV.FEEDBACK_BOARD_ID}
publishableKey={ENV.BSW_PUBLISHABLE_KEY}
user={user}
/>Mode resolution: explicit adapter → Hosted when boardId + publishable key → otherwise local.
Pitfalls
- Rendering Feedback without a client boundary
- Forgetting styles.css
- Leaking secret keys through the root loader ENV blob
- Trusting client author ids on resource routes
- Using
completedinstead ofshipped
Related: React guide · Next.js guide · Identity · Adapter · Quickstart