ClickHouse Next.js

Build ClickHouse analytics into Next.js without duplicating query logic

This page is about the App Router split: some analytics should run directly in server components, some should be exposed to browser clients, and you do not want those two paths to invent different versions of the same query.

Framework fit

Next.js App Router

Execution model

Local or HTTP

Best for

Dashboards and product analytics

App Router makes it easy to fork the same query

A metric often starts in a server component, then gets copied into a route handler for client-side refresh, then appears again in another page. Next.js makes both paths easy, which is exactly why they drift.

Server and browser consumers need different delivery

The server can call code directly. The browser cannot. The awkward part is keeping those two access modes aligned without rewriting the query for each one.

Route files become accidental analytics files

If the only reusable unit is a loose SQL string, route files and page files start accumulating validation, parsing, and analytics logic that should live elsewhere.

The App Router shape

Keep the query definition outside the route and page files

The cleanest Next.js setup is one shared query definition plus two delivery paths: local execution for server code and a thin App Router handler for browser consumers.

  • Generate schema types from your ClickHouse database
  • Define reusable analytics queries in TypeScript
  • Mount the same API under app/api/* with createFetchHandler
  • Run those queries directly in server components and actions
  • Add React hooks later without changing the server contract

App Router

One route file that stays thin

the-app-router-shape.ts

The route exists to expose the query, not to become the place where the query is authored.

Server-first usage

Run local in server code, use HTTP only where the browser needs it

The big advantage of Next.js is that not every query needs to go through the network. Use direct execution in server components by default, and reserve HTTP for interactive client-side flows.

That server-first split is what makes this page different from the React page. The React page starts at the client hook layer. This page starts at the question of whether the query should leave the server at all.

If browser hooks are the main concern, continue to the React page. If the concern is general backend reuse outside Next.js, use the Node.js page.

Server component

A server component calling the shared query directly

server-first-usage.ts

The component gets the data without a self-HTTP call, but it is still using the same analytics definition the browser path can expose later.

Where teams usually get stuck

The questions this page should answer

What this page is really answering

Not just how to connect ClickHouse to Next.js, but where query logic should live when the framework gives you both server and client execution paths.

Where the duplication usually starts

Usually when a server-rendered dashboard later needs client refresh or filtering and teams copy the query into a route instead of exposing the shared definition cleanly.

Why App Router changes the advice

Because you can often skip HTTP inside the app itself. That is why the local-vs-HTTP split matters more here than on the generic REST page.

Where to go next

Use the React page when the client hook layer is the next problem, not the current one.

Next step

Start with the Next.js recipe, then layer in React hooks where they help

Get the App Router setup correct first. After that, decide which analytics belong in server components and which need a browser-facing client layer.