/** * `@nifrajs/web-react/router` - React routing bindings over the agnostic `@nifrajs/web` router: * ``/``, `useNavigate`, `useParams`, `useLocation`, `useSearchParams`, and ``. * * These read the current route from a {@link RouterContext} that `compose` provides on BOTH the SSR * render and the client mount (seeded from the request match / router state respectively), so the read * hooks (`useParams`/`useLocation`/`useSearchParams`) are SSR-correct and hydrate with no mismatch. * Navigation goes through `@nifrajs/web`'s DOM-free navigate bridge (`getBrowserNavigate`, populated by * `installHistory`) - so this module imports only `react` (never `react-dom/*`), and a route component * can use these on the server and the client without dragging a DOM build into the wrong bundle. No JSX * (the package builds with plain `tsc`), so everything is `createElement`. */ import type { InferOutput, StandardSchemaV1 } from "@nifrajs/core/server"; import type { Blocker, BlockerFunction, BlockerState, NavigateFunction, NavigateOptions } from "@nifrajs/web"; import { type AnchorHTMLAttributes, type CSSProperties, type ReactNode } from "react"; export type { Blocker, BlockerFunction, BlockerState, NavigateFunction }; /** The current route the routing hooks read. Provided by `compose` on SSR + client mount alike. */ export interface RouterContextValue { /** The matched route's decoded path params (`/users/:id` → `{ id: "7" }`). */ readonly params: Readonly>; /** The current URL's `pathname + search` (no hash - the router never carries one). */ readonly path: string; /** The route's typed, validated search params (the loader's `ctx.search`), derived from the URL via * the shared `searchOf` on SSR + client alike, read by {@link useSearch}. `{}` when the route * declares no `searchSchema` (then it is the raw parsed query) or outside a nifra route tree. */ readonly search: Record; /** True while a client navigation (or revalidation) is in flight - the current route stays mounted * until the new one is ready. Always `false` on SSR (loaders block before render). Drives loading UI * via {@link useNavigation}. */ readonly pending: boolean; /** The `pathname + search` a navigation is transitioning TO while `pending` (`undefined` when idle or * during a same-route revalidation). Powers {@link NavLink}'s per-link `isPending`. */ readonly pendingPath?: string | undefined; } export declare const RouterContext: import("react").Context; /** * The matched route's decoded path params - `/users/:id` on `/users/7` → `{ id: "7" }`. SSR-correct: * `compose` provides the same value server-side (from the request match) and client-side (from router * state), so a param rendered into markup doesn't flash on hydration. */ export declare function useParams = Record>(): Readonly; /** * The route's typed, validated search params: the SAME value the loader received as `ctx.search`. * SSR-correct: `compose` provides it from `searchOf(searchSchema, url.search)` server-side and from the * identical derivation on the client mount, so a value rendered from it doesn't flash on hydration. * Hostile input already failed closed to the schema's defaults at match time, so a component never * parses `window.location.search` or guards against a bad query. * * Pass the route's `searchSchema` as the type argument to get its output type; bare, it's the raw parsed * query (`Record`), which is also what a route without a `searchSchema` yields. * * ```tsx * export const searchSchema = v.object({ page: v.optional(v.fallback(v.number(), 1), 1) }) * const { page } = useSearch() // page: number * ``` */ export declare function useSearch(): Schema extends StandardSchemaV1 ? InferOutput : Record; /** The parsed current location. `hash` is always `""` - the fragment is client-only and never reaches * the router state / server, so exposing a live hash would hydration-mismatch; read `window.location.hash` * directly (in an effect) if you truly need it. */ export interface Location { readonly pathname: string; readonly search: string; readonly hash: string; } /** The current {@link Location} (`pathname`/`search`/`hash`), derived from the router context. */ export declare function useLocation(): Location; /** The current navigation state, mirroring the Remix `useNavigation()` shape for familiarity. */ export interface Navigation { /** True while a client navigation (or revalidation) is in flight. The current route stays mounted * until the new one is ready, so this drives a loading indicator, not a route swap. */ readonly pending: boolean; /** `"loading"` while a navigation is in flight, else `"idle"`. */ readonly state: "idle" | "loading"; /** The `pathname + search` being navigated TO while `pending`; `undefined` when idle or during a * same-route revalidation. Mirrors Remix's `useNavigation().location`. */ readonly location: string | undefined; } /** * Observe client navigation to drive loading UI (a top-bar spinner, dimmed content, a skeleton). nifra * navigates imperatively - it fetches the next route's chunk + loader data while the current route stays * on screen, then swaps - so `pending` is the signal for "a transition is in flight," not a Suspense * boundary. Always `{ pending: false, state: "idle" }` on the server (loaders block before render), so * it is hydration-safe. * * ```tsx * const { pending } = useNavigation() * return
{pending && }
* ``` */ export declare function useNavigation(): Navigation; /** Convenience boolean form of {@link useNavigation}: `true` while a client navigation is in flight. */ export declare function usePending(): boolean; /** Get the {@link NavigateFunction} (a string path, a history delta, or a typed `{ to, search }` object; * a render-time navigate isn't valid - use {@link Navigate}, which navigates in an effect). Stable across * renders; resolves the browser navigate at call time (so it works as soon as `installHistory` has run, * and no-ops before then / on the server). */ export declare function useNavigate(): NavigateFunction; /** * Guard navigation away from a page with unsaved work, confirming with your OWN async UI. Mirrors * react-router's `useBlocker`: pass a boolean (`useBlocker(isDirty)`) or a predicate * `({ currentLocation, nextLocation }) => boolean`, and get back a {@link Blocker}. When a navigation * (a ``/anchor click, `useNavigate`, or a browser back/forward) is intercepted, `blocker.state` * becomes `"blocked"` and `proceed`/`reset` go live - render a dialog and call `proceed()` to continue * or `reset()` to stay put. It also arms the browser's native "Leave site?" prompt on tab close / reload. * Idle (never blocks, `proceed`/`reset` are `undefined`) on the server and before hydration, so it's * SSR-safe and hydration-stable. * * ```tsx * const blocker = useBlocker(form.isDirty) * return ( * <> * * {blocker.state === "blocked" && ( * * )} * * ) * ``` */ export declare function useBlocker(shouldBlock: boolean | BlockerFunction): Blocker; /** The value forms `setSearchParams` accepts. */ export type SearchParamsInit = URLSearchParams | Record | string; /** Set the query string. Accepts a `URLSearchParams`, a record, a raw string, or an updater of the * current params; navigates to the same pathname with the new query (push, or replace via options). */ export type SetSearchParams = (next: SearchParamsInit | ((prev: URLSearchParams) => SearchParamsInit), options?: NavigateOptions) => void; /** * The current query as a `URLSearchParams` (SSR-correct via the router context) plus a setter that * navigates to the new query. Mirrors react-router's `useSearchParams` tuple. */ export declare function useSearchParams(): readonly [URLSearchParams, SetSearchParams]; /** {@link Link} props: every `` attribute except `href` (set from `to`), plus `to` + `replace`. */ export interface LinkProps extends Omit, "href"> { /** Same-origin destination path (e.g. `/users/7?tab=posts`). Rendered as the ``. */ readonly to: string; /** Replace the current history entry instead of pushing. */ readonly replace?: boolean; } /** * A client-navigating anchor. Renders a real `` (so it's a working link before hydration * and for right-click / open-in-new-tab), and on a plain left-click navigates through the router * instead of a full reload. Calling `navigate` + `preventDefault` here means `installHistory`'s * document-level click handler sees `defaultPrevented` and stands down - exactly one navigation. */ export declare const Link: import("react").ForwardRefExoticComponent>; /** The state a {@link NavLink}'s function-form `className`/`style`/`children` receive. */ export interface NavLinkRenderProps { /** True when the current location matches this link's `to` (prefix match, or exact when `end`). */ readonly isActive: boolean; /** True while a client navigation to THIS link's target is in flight (matched like `isActive`). * `false` on SSR and when idle. Use it to show a per-link spinner during the transition. */ readonly isPending: boolean; } /** {@link NavLink} props - like {@link LinkProps}, but `className`/`style`/`children` may be functions * of the active state, and `end`/`caseSensitive` tune matching. */ export interface NavLinkProps extends Omit { /** Match the full path exactly instead of as a prefix (use for `to="/"` so it isn't always active). */ readonly end?: boolean; /** Match case-sensitively (default: case-insensitive, like the DOM). */ readonly caseSensitive?: boolean; readonly className?: string | ((props: NavLinkRenderProps) => string | undefined); readonly style?: CSSProperties | ((props: NavLinkRenderProps) => CSSProperties | undefined); readonly children?: ReactNode | ((props: NavLinkRenderProps) => ReactNode); } /** * A {@link Link} that knows whether it points at the current location. Adds `aria-current="page"` when * active and resolves function-form `className`/`style`/`children` with `{ isActive, isPending }`. * Default matching is prefix-on-segment-boundary (so `/users` is active on `/users/7`); pass `end` for * an exact match. */ export declare const NavLink: import("react").ForwardRefExoticComponent>; /** {@link Navigate} props: the destination `to` and whether to `replace` the history entry. */ export interface NavigateProps { readonly to: string; readonly replace?: boolean; } /** * Declaratively navigate on mount - the component analogue of `useNavigate` (e.g. a guard that renders * ``). Navigates in an effect, so it's a safe no-op during SSR (renders * `null`); the redirect happens once on the client after hydration. */ export declare function Navigate({ to, replace }: NavigateProps): null; //# sourceMappingURL=router.d.ts.map