API Reference
Complete reference for HadarsOptions, hooks, and TypeScript types.
HadarsOptions
Defined in hadars.config.ts at your project root.
entrystringPath to your page component. Required.portnumberHTTP port. Default: 9090.hmrPortnumberrspack HMR dev server port. Default: port + 1.baseURLstringPublic base path, e.g. "/app". Default: "".workersnumberWorker processes forked in run() mode (Node.js only). Default: 1.proxyRecord / fnPath-prefix proxy rules or a custom async function.proxyCORSbooleanInject CORS headers on proxied responses.defineRecordCompile-time constants for rspack's DefinePlugin.swcPluginsarrayExtra SWC plugins (e.g. Relay compiler).moduleRulesarrayExtra rspack module rules appended to the built-in set (client + SSR).fetchfunctionCustom fetch handler. Return a Response to short-circuit SSR.websocketobjectWebSocket handler (Bun only), forwarded to Bun.serve.wsPathstringPath that triggers WebSocket upgrade. Default: "/ws".htmlTemplatestringPath to a custom HTML template with HADARS_HEAD / HADARS_BODY markers.optimizationobjectOverride rspack optimization for production client builds.cachefunctionSSR response cache for run() mode. Return { key, ttl? } to cache a request.pathsfunctionReturns URL list to pre-render with hadars export static. Receives HadarsStaticContext.sourcesarrayGatsby-compatible source plugins. hadars infers a GraphQL schema from their nodes automatically.graphqlfunctionCustom GraphQL executor. Passed to paths(), getInitProps(), and useGraphQL().onErrorfunctionCalled on every SSR render error. Use to forward to Sentry, Datadog, etc. May be async — hadars fires it without awaiting.Example
import os from 'os';
import * as Sentry from '@sentry/node';
import type { HadarsOptions } from 'hadars';
const config: HadarsOptions = {
entry: 'src/App.tsx',
port: 3000,
workers: os.cpus().length,
// Proxy /api/* to an internal service
proxy: { '/api': 'http://localhost:4000' },
// Short-circuit specific paths
fetch: async (req) => {
if (req.pathname === '/health') {
return new Response('ok');
}
},
// Cache pages by pathname for 60 seconds, skip authenticated users
cache: (req) => req.cookies.session
? null
: { key: req.pathname, ttl: 60_000 },
// Expose a build-time constant to client bundles
define: { '__APP_VERSION__': JSON.stringify('1.2.3') },
// Forward SSR errors to your monitoring service
onError: (err, req) => Sentry.captureException(err, {
extra: { url: req.url, method: req.method },
}),
};
export default config;HadarsRequest
The request object passed to getInitProps and the fetch handler:
interface HadarsRequest extends Request {
pathname: string; // URL pathname, e.g. "/blog/my-post"
search: string; // Query string, e.g. "?page=2&sort=asc"
location: string; // pathname + search
cookies: Record<string, string>;
}HadarsApp
The type for your default-exported page component:
type HadarsApp<T> = React.FC<T & {
location: string; // current URL (pathname + search)
}>;
// Usage
const App: HadarsApp<Props> = ({ user, location }) => (
<>
<HadarsHead status={200}><title>{user.name}</title></HadarsHead>
...
</>
);Lifecycle hooks
// Runs on the server for every request.
// Return value becomes the props passed to your App component.
export const getInitProps = async (req: HadarsRequest): Promise<Props> => {
return { user: await db.getUser(req.cookies.session) };
};
// Runs on the server after the final SSR render.
// Strip server-only fields before props are JSON-serialised for the client.
export const getFinalProps = async (props: Props): Promise<PublicProps> => {
const { dbConnection, ...rest } = props;
return rest;
};
// Runs in the browser after the page loads.
// Return value replaces/extends props before hydrateRoot is called.
export const getClientProps = async (props: PublicProps): Promise<ClientProps> => {
return {
...props,
theme: localStorage.getItem('theme') ?? 'dark',
};
};useServerData
function useServerData<T>(
fn: () => Promise<T> | T,
options?: { cache?: boolean }
): T | undefinedFetches async data during SSR inside any component. Returns undefined on the first pass(es) while the promise is in-flight, then the resolved value on the final pass. On the client, reads from the hydration cache — fn is never called in the browser. The cache key is derived automatically from the call-site's position in the component tree via useId().
For client-side navigation, fires a single GET <url> with Accept: application/json and suspends via React Suspense until resolved. All useServerData calls within the same render share one request.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| cache | boolean | true | When false, the cached value is evicted when the component unmounts so the next mount fetches fresh data from the server. Safe with React Strict Mode. |
// cache: false — next mount always fetches fresh data from the server
const uptime = useServerData(() => process.uptime(), { cache: false });useGraphQL
// Typed — result.data shape is inferred from the DocumentNode
function useGraphQL<TData, TVariables>(
query: HadarsDocumentNode<TData, TVariables>,
variables?: TVariables,
): { data?: TData } | undefined
// Untyped fallback for plain query strings
function useGraphQL(
query: string,
variables?: Record<string, unknown>,
): { data?: Record<string, unknown> } | undefinedExecutes a GraphQL query inside any component using the executor configured via sources or graphql in hadars.config.ts. Integrates with useServerData — the query runs on the server during SSR/export and is hydrated on the client at no extra cost. Returns undefined while pending. GraphQL errors throw, marking the page as failed during static export.
loadModule
// Browser: becomes import('./path') — automatic code splitting
// Server: becomes Promise.resolve(require('./path')) — statically bundled
const HeavyChart = React.lazy(() => loadModule<{ default: React.FC }>('./HeavyChart'));Transformed at compile time by the hadars rspack loader. The module is included in the server bundle statically and downloaded on demand in the browser.