CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-router-dom

Declarative routing for React web applications

Pending
Overview
Eval results
Files

server-side-rendering.mddocs/

Server-Side Rendering

Components and utilities for server-side rendering including static routing, data loading on the server, and hydration support.

Capabilities

StaticRouter

Router component for server-side rendering with a fixed location.

/**
 * Router component for server-side rendering
 * @param props - StaticRouter configuration options
 * @returns Router component with static location
 */
function StaticRouter(props: StaticRouterProps): JSX.Element;

interface StaticRouterProps {
  /** Base URL for all routes */
  basename?: string;
  /** Child routes and components */
  children?: React.ReactNode;
  /** Current location for rendering */
  location: Partial<Location> | string;
}

interface Location {
  pathname: string;
  search: string;
  hash: string;
  state: any;
  key: string;
}

Usage Example:

import { StaticRouter } from "react-router-dom";
import { renderToString } from "react-dom/server";

// Server-side rendering
export function render(request: Request) {
  const html = renderToString(
    <StaticRouter location={request.url}>
      <App />
    </StaticRouter>
  );
  
  return `
    <!DOCTYPE html>
    <html>
      <head><title>My App</title></head>
      <body>
        <div id="root">${html}</div>
        <script src="/client.js"></script>
      </body>
    </html>
  `;
}

StaticRouterProvider

Provider component for static routers with data loading capabilities.

/**
 * Provider component for static routers with data support
 * @param props - StaticRouterProvider configuration
 * @returns Router provider with static handler context
 */
function StaticRouterProvider(props: StaticRouterProviderProps): JSX.Element;

interface StaticRouterProviderProps {
  /** Router instance from createStaticRouter */
  router: DataRouter;
  /** Static handler context with data */
  context: StaticHandlerContext;
  /** Whether to hydrate on client */
  hydrate?: boolean;
  /** Nonce for CSP */
  nonce?: string;
}

interface StaticHandlerContext {
  /** Base URL path */
  basename: string;
  /** Current location */
  location: Location;
  /** Matched routes */
  matches: StaticHandlerMatch[];
  /** Data from route loaders */
  loaderData: Record<string, any>;
  /** Data from route actions */
  actionData: Record<string, any> | null;
  /** Route errors */
  errors: Record<string, any> | null;
  /** HTTP status code */
  statusCode: number;
  /** Response headers from loaders */
  loaderHeaders: Record<string, Headers>;
  /** Response headers from actions */
  actionHeaders: Record<string, Headers> | null;
  /** Deferred data promises */
  activeDeferreds: Record<string, DeferredData> | null;
}

Usage Example:

import { 
  createStaticRouter, 
  createStaticHandler,
  StaticRouterProvider 
} from "react-router-dom";

export async function render(request: Request) {
  const handler = createStaticHandler(routes);
  const context = await handler.query(request);
  
  if (context instanceof Response) {
    return context; // Handle redirects
  }
  
  const router = createStaticRouter(routes, context.location);
  
  const html = renderToString(
    <StaticRouterProvider router={router} context={context} />
  );
  
  return new Response(html, {
    status: context.statusCode,
    headers: { "Content-Type": "text/html" },
  });
}

HydratedRouter

Specialized router for React Server Components and framework-mode applications that hydrates from server state.

/**
 * Framework-mode router for hydrating from server state
 * @param props - HydratedRouter configuration options
 * @returns Hydrated router component for SSR applications
 */
function HydratedRouter(props: HydratedRouterProps): JSX.Element;

interface HydratedRouterProps {
  /** Context function for clientAction/clientLoader */
  unstable_getContext?: RouterInit["unstable_getContext"];
  /** Error handler for application errors */
  unstable_onError?: (error: any, errorInfo: React.ErrorInfo) => void;
}

interface RouterInit {
  /** Function to get context for client-side route functions */
  unstable_getContext?: () => any;
}

Usage Example:

import { HydratedRouter } from "react-router-dom";

// Client-side entry point for framework apps
function App() {
  return (
    <HydratedRouter 
      unstable_onError={(error, errorInfo) => {
        console.error("App error:", error, errorInfo);
        reportError(error);
      }}
    />
  );
}

ServerRouter

Server-side router component for React Server Components.

/**
 * Server router component for React Server Components
 * @param props - ServerRouter configuration
 * @returns Server-side router for RSC applications
 */
function ServerRouter(props: ServerRouterProps): JSX.Element;

interface ServerRouterProps {
  /** Current request context */
  context: EntryContext;
  /** Request URL */
  url: string;
  /** Abort signal for request cancellation */
  abortDelay?: number;
  /** Nonce for content security policy */
  nonce?: string;
}

interface EntryContext {
  /** Server manifest */
  manifest: AssetsManifest;
  /** Route modules */
  routeModules: RouteModules;
  /** Server build */
  serverBuild: ServerBuild;
  /** Static handler context */
  staticHandlerContext: StaticHandlerContext;
}

Document Components

Components for managing document head and scripts in SSR applications.

/**
 * Component for rendering document metadata
 * @param props - Meta component options
 * @returns Meta tags for document head
 */
function Meta(props?: { nonce?: string }): JSX.Element;

/**
 * Component for rendering document links
 * @param props - Links component options  
 * @returns Link tags for document head
 */
function Links(props?: LinksProps): JSX.Element;

/**
 * Component for rendering application scripts
 * @param props - Scripts component options
 * @returns Script tags for document body
 */
function Scripts(props?: ScriptsProps): JSX.Element;

/**
 * Component for prefetching page links
 * @param props - PrefetchPageLinks options
 * @returns Prefetch link tags
 */
function PrefetchPageLinks(props?: { page?: string }): JSX.Element;

interface LinksProps {
  /** Nonce for CSP */
  nonce?: string;
}

interface ScriptsProps {
  /** Nonce for CSP */
  nonce?: string;
  /** Async script loading */
  async?: boolean;
  /** Cross-origin setting */
  crossOrigin?: string;
}

Usage Example:

import { Links, Meta, Scripts } from "react-router-dom";

function Document({ children, title }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1" />
        <title>{title}</title>
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <Scripts />
      </body>
    </html>
  );
}

Route Module Functions

Server-side functions for generating metadata and handling requests.

/**
 * Function for generating document metadata
 */
type MetaFunction<
  Loader = unknown,
  ParentsLoaders extends Record<string, unknown> = {}
> = (args: MetaArgs<Loader, ParentsLoaders>) => MetaDescriptor[];

interface MetaArgs<
  Loader = unknown,
  ParentsLoaders extends Record<string, unknown> = {}
> {
  /** Route data from loader */
  data: Loader;
  /** Route parameters */
  params: Params;
  /** Current location */
  location: Location;
  /** Parent route data */
  matches: UIMatch<unknown, unknown>[];
  /** Error from route (if any) */
  error?: unknown;
}

type MetaDescriptor = 
  | { title: string }
  | { name: string; content: string }
  | { property: string; content: string }
  | { httpEquiv: string; content: string }
  | { charset: string }
  | { [key: string]: string };

/**
 * Function for generating document links
 */
type LinksFunction = () => LinkDescriptor[];

type LinkDescriptor = 
  | PageLinkDescriptor
  | HtmlLinkDescriptor;

interface PageLinkDescriptor {
  page: string;
  [key: string]: unknown;
}

interface HtmlLinkDescriptor {
  rel: string;
  href?: string;
  [key: string]: unknown;
}

/**
 * Function for generating response headers
 */
type HeadersFunction = (args: HeadersArgs) => Headers | HeadersInit;

interface HeadersArgs {
  loaderHeaders: Headers;
  parentHeaders: Headers;
  actionHeaders: Headers;
  errorHeaders: Headers | undefined;
}

Usage Examples:

// Route with metadata
export const meta: MetaFunction<typeof loader> = ({ data, location }) => {
  return [
    { title: data.post.title },
    { 
      name: "description", 
      content: data.post.excerpt 
    },
    {
      property: "og:title",
      content: data.post.title,
    },
    {
      property: "og:url",
      content: `https://example.com${location.pathname}`,
    },
  ];
};

// Route with links
export const links: LinksFunction = () => {
  return [
    { rel: "stylesheet", href: "/styles/post.css" },
    { rel: "prefetch", href: "/api/related-posts" },
  ];
};

// Route with headers
export const headers: HeadersFunction = ({ loaderHeaders }) => {
  return {
    "Cache-Control": "public, max-age=3600",
    "Vary": "Accept-Encoding",
    ...Object.fromEntries(loaderHeaders.entries()),
  };
};

Client-Side Functions

Functions that run on the client for enhanced interactivity.

/**
 * Client-side loader function
 */
type ClientLoaderFunction<T = any> = (
  args: ClientLoaderFunctionArgs
) => Promise<T> | T;

interface ClientLoaderFunctionArgs {
  request: Request;
  params: Params;
  serverLoader: () => Promise<any>;
}

/**
 * Client-side action function  
 */
type ClientActionFunction<T = any> = (
  args: ClientActionFunctionArgs
) => Promise<T> | T;

interface ClientActionFunctionArgs {
  request: Request;
  params: Params;
  serverAction: () => Promise<any>;
}

Usage Examples:

// Client loader that enhances server data
export const clientLoader: ClientLoaderFunction = async ({ 
  serverLoader, 
  params 
}) => {
  const serverData = await serverLoader();
  
  // Add client-specific data
  const clientData = {
    ...serverData,
    clientTimestamp: Date.now(),
    isOnline: navigator.onLine,
  };
  
  return clientData;
};

// Client action with optimistic updates
export const clientAction: ClientActionFunction = async ({ 
  request, 
  serverAction 
}) => {
  const formData = await request.formData();
  
  // Optimistic update
  updateUIOptimistically(formData);
  
  try {
    return await serverAction();
  } catch (error) {
    // Revert optimistic update
    revertOptimisticUpdate();
    throw error;
  }
};

Testing Utilities

Utilities for testing server-side rendered components.

/**
 * Create route stub for testing
 * @param routes - Route definitions for testing
 * @param opts - Test configuration options
 * @returns Component for testing routes
 */
function createRoutesStub(
  routes: RouteObject[],
  opts?: {
    basename?: string;
    initialEntries?: string[];
    initialIndex?: number;
  }
): React.ComponentType<RoutesTestStubProps>;

interface RoutesTestStubProps {
  children?: React.ReactNode;
}

Usage Example:

import { createRoutesStub } from "react-router-dom";
import { render, screen } from "@testing-library/react";

const RouteStub = createRoutesStub([
  {
    path: "/users/:id",
    element: <UserProfile />,
    loader: ({ params }) => ({ id: params.id, name: "John Doe" }),
  },
]);

test("renders user profile", async () => {
  render(<RouteStub initialEntries={["/users/123"]} />);
  
  expect(await screen.findByText("John Doe")).toBeInTheDocument();
});

SSR Patterns

Basic Server Setup

// server.ts
import { createStaticHandler, createStaticRouter } from "react-router-dom";

const handler = createStaticHandler(routes);

export async function handleRequest(request: Request) {
  const context = await handler.query(request);
  
  if (context instanceof Response) {
    return context;
  }
  
  const router = createStaticRouter(routes, context.location);
  
  const html = renderToString(
    <StaticRouterProvider router={router} context={context} />
  );
  
  return new Response(
    `<!DOCTYPE html><html><body>${html}</body></html>`,
    {
      status: context.statusCode,
      headers: { "Content-Type": "text/html" },
    }
  );
}

Data Loading on Server

// Route with server data loading
export const loader = async ({ request, params }) => {
  const user = await fetchUser(params.id);
  const posts = await fetchUserPosts(params.id);
  
  return json({ user, posts }, {
    headers: {
      "Cache-Control": "public, max-age=300",
    },
  });
};

// Client hydration
const router = createBrowserRouter(routes);

hydrateRoot(
  document.getElementById("root"),
  <RouterProvider router={router} />
);

Error Handling in SSR

// Error boundary for SSR
function RootErrorBoundary() {
  const error = useRouteError();
  
  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>{error.status} {error.statusText}</h1>
        <p>{error.data}</p>
      </div>
    );
  }
  
  return (
    <div>
      <h1>Something went wrong!</h1>
      <pre>{error.message}</pre>
    </div>
  );
}

Install with Tessl CLI

npx tessl i tessl/npm-react-router-dom

docs

data-loading-hooks.md

index.md

navigation-components.md

navigation-hooks.md

route-configuration.md

router-components.md

router-creation.md

server-side-rendering.md

utilities.md

tile.json