Choose where to save feedback

Save feedback in your browser, share it through your own API, or follow the upcoming Artemis72 service.

Tip

Help me follow the Choose where to save feedback guide. Save feedback in your browser, share it through your own API, or follow the upcoming Artemis72 service. Review the current project and turn this guidance into concrete next steps.

CHOOSE

Pick a setup

OptionChoose this whenWhat you need
localStorageYou work alone or want to try itNo backend
APIYour team needs shared feedbackYour own backend
Artemis72You want a managed serviceComing soon

Not sure which one to choose? Start with localStorage. Move to API later when the team needs to see the same feedback across browsers.

DEFAULT

Use localStorage without a backend

Add this once near the root of your app. Feedback will be saved in the current browser.

tsx
<FivePixels  project={{ id: "my-app" }}  visibility={{ devOnly: true }}/>

No API or Adapter is needed. You should see the Fivepixels button after the page loads.

localStorage only saves data in the current browser. Use API mode when teammates need to see the same feedback.

visibility.devOnly hides Fivepixels in production builds. Turn it off if your staging site is built in production mode.

Connect your API

API mode saves feedback through your backend so the team can share it. A FivePixelsAdapter is the small connection between Fivepixels and your API. Add the three basic handlers below and use sync="api".

Start with these three required functions to load, create, and update shared feedback.

tsx
import { useMemo } from "react";import { FivePixels, type FivePixelsAdapter } from "@fivepixels-js/react";
function createAdapter(): FivePixelsAdapter {  const base = "/api/v1/fivepixels/projects/my-app";  const write = (method: "POST" | "PATCH", body: unknown) => ({    method,    headers: { "Content-Type": "application/json" },    body: JSON.stringify(body),  });
  return {    markers: {      list: ({ pathname }) =>        fetch(`${base}/feedbacks/markers?pathname=${encodeURIComponent(pathname)}`)          .then((response) => response.json()),    },    feedback: {      create: (payload) =>        fetch(`${base}/feedbacks`, write("POST", payload))          .then((response) => response.json()),      update: (id, payload) =>        fetch(`${base}/feedbacks/${id}`, write("PATCH", payload))          .then((response) => response.json()),    },  };}
export function App() {  const adapter = useMemo(() => createAdapter(), []);
  return (    <FivePixels      project={{ id: "my-app", env: "stage" }}      sync="api"      adapter={adapter}    />  );}

Create the Adapter once with useMemo or outside the component. Creating it again on every render can repeat requests.

More API examples

Choose how reviewers sign in

API sharing asks reviewers to sign in by default. Add auth.login to the Adapter above.

tsx
const adapterWithLogin: FivePixelsAdapter = {  ...createAdapter(),  auth: {    login: async (credentials) => {      const response = await fetch("/api/v1/fivepixels/auth/login", {        method: "POST",        headers: { "Content-Type": "application/json" },        body: JSON.stringify(credentials),      });      if (!response.ok) throw new Error("Login failed");      return response.json();    },  },};
<FivePixels sync="api" adapter={adapterWithLogin} />

Connect your team tools

Run your code when feedback changes, and send selected work to a GitHub Issue.

tsx
<FivePixels  sync="api"  adapter={adapter}  onEvent={(event) => console.info("fivepixels", event)}  github={{    enabled: true,    modes: ["from-list"],    onCreate: async (feedback) => {      const response = await fetch("/api/github/issues", {        method: "POST",        body: JSON.stringify(feedback),      });      const issue = await response.json();      return { issueNumber: issue.number, issueUrl: issue.html_url };    },  }}/>

Share existing feedback with your team

Keep the same project id when you move from localStorage to API. Choose the environment and version together so everyone sees the expected feedback.

tsx
// Before: feedback stays in this browser.<FivePixels project={{ id: "my-app" }} sync="local" />
// After: new feedback uses your shared API.<FivePixels  project={{ id: "my-app", env: "stage", version: "1.3.0" }}  sync="api"  adapter={adapter}/>

Switching to API does not copy old browser feedback automatically. Move it with JSON Export/Import, or create it again in the shared API.

Artemis72 and what you build

Artemis72 is a planned hosted service for Fivepixels. It is still in preparation, so setup code is not available yet.

Required API functions

  1. markers.list({ pathname }) returns ReportFeedback[].
  2. feedback.create(payload) returns ReportFeedback with a server id.
  3. Implement feedback.update or cases.update.
FunctionWhat it does
markers.listRequired for markers on the current pathname
feedback.createRequired to persist new feedback
feedback.update or cases.updateAt least one update path is required
auth · replies · members · sessionOptional extensions based on your workflow

For a shared API without login, set require={{ authLogin: false, reviewerKey: false }}. Authentication handlers are not used in this mode.

You build and run the API routes, database, sign-in, and permissions. Fivepixels connects to them through the frontend Adapter.