Hindsight, Pro
Product analytics for Next.js that opens on what's happening right now. Install it into a new project, then rewrite its sample API to read your own events.
Hindsight is a whole app made of Dashboardblocks blocks: Live, with visits lighting up on a map, an Overview board, Trends, Funnels, Retention and Paths to explore events, People with saved segments and a page for each person, Setup for the tracking snippet, and Ask AI on every page. Every page reads and writes through one sample API, so the app works as soon as it's installed. Open the preview to try it first.
Install
Hindsight comes with All-Access and installs with the shadcn CLI.
-
Make a Next.js project with shadcn set up, at shadcn create (opens in a new tab) or with the CLI:
npx shadcn@latest init --template next -
Add the
@dashboardblocks-proregistry and your token, as Installation shows. -
Install Hindsight:
npx shadcn@latest add @dashboardblocks-pro/hindsight-next --overwrite -
Run
npm run devand open/live.
With --overwrite, the CLI replaces any file you have that Hindsight also ships, such as shadcn components and blocks you've changed and a proxy.ts, so commit first and check the diff. Without it, the CLI asks about each of those files, and skips them all when it can't ask. Your root layout, home page and theme stay as they are, and Hindsight takes your shadcn style, component library and icon set.
What it adds
| Path | What's there |
|---|---|
app/(hindsight)/ | The routes. Each one loads its page's data from the sample API and passes it to the page. |
components/hindsight/pages/ | A component per page. They take data and callbacks as props and know nothing about Next.js. |
lib/hindsight/api/ | The sample API: async functions in index.ts and their types in types.ts. The *-read.ts files build the sample answers from the sample data in data/, and people-segments.ts checks the segments people save. |
components/hindsight/next/ | The layout, the session, each page's Server Actions, and the timer that refreshes Live and Setup. |
components/hindsight/routes.ts | The list of pages that the sidebar, breadcrumbs and ⌘K search read. |
components/hindsight/ | The parts every page shares: the app shell, the logo, the link adapter, the Ask AI panel, the person peek, and the People filters the sample API also reads. |
lib/hindsight/ | The CSV download that People uses. |
proxy.ts | Runs Hindsight's proxy on its pages. |
In a project with a src/ folder, they go in src/. The blocks Hindsight uses install next to your other blocks in components/dashboardblocks/.
Use your own data
Pages never load data themselves. Each route file calls functions from lib/hindsight/api, and each change goes through a Server Action that calls one too. To show your own events, rewrite the bodies of those functions and keep their types. The pages, routes and actions stay as they are.
- Start with the page you need first. Its route file in
app/(hindsight)/names the functions it calls, such asgetLivefor Live. - Rewrite each function to query your event store, such as ClickHouse, BigQuery or your analytics provider's API, and return the type
types.tsgives it. A write returns a result the page shows, such as a refusal with its reason, so keep returning one. - Once nothing reads
data/any more, delete it and the*-read.tsfiles. Keeppeople-segments.tsif you want its checks on a segment's name and conditions.
Every read and write takes the session first, which names the project the person opened. The sidebar's person and its project switcher come from getShell's user and projects.
Live refreshes every 3 seconds, and Setup every 5 while a check waits for events, and each refresh reads the page and the shell again. Change seconds on LiveRefresh in their route files to suit what your queries cost.
Keep what people save
The sample API keeps nothing. Saved segments, Setup's test events and checks, and the project someone last opened are kept in cookies by components/hindsight/next/session.ts and the Server Actions, so the app works without a database. Each place says what keeps it in your app: move segments to a table, and the rest to your account and event store. The Overview board isn't kept at all. Its layout goes in saveLayout, removeCards and restoreCards, and Save to Overview from the explorers in saveToOverview, saveFunnelToOverview, saveRetentionToOverview and savePathsToOverview, all in index.ts. getOverview then reads the board back with its cards.
Connect Ask AI to your model
ask in index.ts streams an answer as AI SDK message parts: the tools the model called and their results, the text, sources, and the questions to ask next. The sample answers the questions each page suggests from the project's own reads. Each item it yields is the whole reply so far. To use your model, install ai and return readUIMessageStream({ stream: streamText(…).toUIMessageStream() }), with tools that call the same read functions. explainAnomaly streams the Overview's anomaly explanation as text, so return streamText(…).textStream there.
Add your auth
components/hindsight/next/session.ts tells every page whether someone is signed in. For now everyone is, until they sign out. Change isSignedOut to read your auth library's session, and add the signed-in person to the session getSession returns.
resolveProject in index.ts is the only check that someone may open a project: sessionFor in components/hindsight/next/project-session.ts calls it before every Server Action, and openProject before switching. Today it accepts any sample project. Make it check the project against the signed-in person's own, and make it async with those two callers once it reads your database.
The /sign-in and /sign-out routes in proxy.ts stand in for your auth library's own. Once you add yours, take them out of its matcher and out of components/hindsight/next/proxy.ts, and point the sign-in and sign-out links in components/hindsight/next/next-shell.tsx at your library's pages.
If you had a proxy.ts before, put yours back and call hindsightProxy(request) from components/hindsight/next/proxy.ts for the paths in the matcher of the one Hindsight installed.