sdk/client | RedwoodSDK
RedwoodSDK

sdk/client

Client Side Functions


The rwsdk/client module provides a set of functions for client-side operations.

initClient

The initClient function is used to initialize the React Client. This hydrates the RSC flight payload that's add at the bottom of the page. This makes the page interactive.

Parameters

initClient() accepts an optional configuration object:

ParameterTypeDescription
transportTransportCustom transport for server communication (defaults to fetchTransport)
hydrateRootOptionsHydrationOptionsOptions passed directly to React's hydrateRoot. Supports all React 19 hydration options including error handling (see examples below).
handleResponse(response: Response) => booleanCustom response handler for navigation errors (navigation GETs)
onHydrated() => voidCallback invoked after a new RSC payload has been committed on the client
onActionResponse(actionResponse) => boolean | voidOptional hook invoked when an action returns a Response; return true to signal that the response has been handled and default behaviour should be skipped

Error Handling

React 19 introduced powerful error handling APIs that you can use via hydrateRootOptions:

  • onUncaughtError: Handles uncaught errors (async errors, event handler errors, errors that escape error boundaries)
  • onCaughtError: Handles errors caught by error boundaries
  • onRecoverableError: Handles recoverable errors during rendering

These handlers are client-side only and do not handle server-side RSC rendering errors or router-level errors.

Usage Examples

Basic usage
import { initClient } from "rwsdk/client";

initClient();
With error handling
import { initClient } from "rwsdk/client";

initClient({
  hydrateRootOptions: {
    onUncaughtError: (error, errorInfo) => {
      console.error("Uncaught error:", error);
      console.error("Component stack:", errorInfo.componentStack);
      // Send to monitoring service
      sendToSentry(error, errorInfo);
    },
    onCaughtError: (error, errorInfo) => {
      console.error("Caught error:", error);
      // Handle errors from error boundaries
      sendToSentry(error, errorInfo);
    },
  },
});
Integration with Sentry
import { initClient } from "rwsdk/client";
import * as Sentry from "@sentry/browser";

initClient({
  hydrateRootOptions: {
    onUncaughtError: (error, errorInfo) => {
      Sentry.captureException(error, {
        contexts: {
          react: {
            componentStack: errorInfo.componentStack,
            errorBoundary: errorInfo.errorBoundary?.constructor.name,
          },
        },
        tags: { errorType: "uncaught" },
      });
    },
    onCaughtError: (error, errorInfo) => {
      Sentry.captureException(error, {
        contexts: {
          react: {
            componentStack: errorInfo.componentStack,
            errorBoundary: errorInfo.errorBoundary?.constructor.name,
          },
        },
        tags: { errorType: "caught" },
      });
    },
  },
});
Custom error recovery
import { initClient } from "rwsdk/client";

initClient({
  hydrateRootOptions: {
    onUncaughtError: (error, errorInfo) => {
      // Log error
      logError(error, errorInfo);

      // Show user-friendly message
      showErrorToast("Something went wrong. Please try again.");

      // Optionally reload the page for critical errors
      if (isCriticalError(error)) {
        window.location.reload();
      }
    },
  },
});
With client-side navigation
import { initClient, initClientNavigation } from "rwsdk/client";

const { handleResponse } = initClientNavigation();
initClient({ handleResponse });

initClientNavigation

The initClientNavigation function is used to initialize the client side navigation. An event handler is assocated to clicking the document. If the clicked element contains a link, href, and the href is a relative path, the event handler will be triggered. This will then fetch the RSC payload for the new page, and hydrate it on the client.

ClientNavigationOptions

initClientNavigation() accepts an optional ClientNavigationOptions object that lets you control how the browser scrolls after each navigation:

OptionTypeDefaultDescription
scrollToTopbooleantrueWhether to scroll to the top of the page after a successful navigation. Set it to false when you want to preserve the existing scroll position (for example, an infinite-scroll list).
scrollBehavior'instant' | 'smooth' | 'auto''instant'How the scroll happens when scrollToTop is true (ignored otherwise).
onNavigate() => Promise<void> | voidCallback executed after the history entry is pushed but before the new RSC payload is fetched. Use it to run custom analytics or side-effects.

Usage Examples

Default behaviour – jump to top instantly
import { initClientNavigation } from "rwsdk/client";

initClientNavigation();
Smooth scrolling to top
initClientNavigation({
  scrollBehavior: "smooth",
});
Preserve scroll position
initClientNavigation({
  scrollToTop: false,
});
Custom onNavigate logic
initClientNavigation({
  scrollBehavior: "auto",
  onNavigate: async () => {
    // e.g. send page-view to analytics before RSC fetch starts
    await myAnalytics.track(window.location.pathname);
  },
});

Rationale & Defaults

RedwoodSDK mirrors the behaviour of classic Multi Page Apps where each link click brings you back to the top of the next page. This is the most common expectation and is therefore the default. You can turn it off or make it smooth with a single option – no additional libraries required.

The navigate function is used to programmatically navigate to a new page. It accepts a href parameter (the destination URL) and an optional options object.

OptionTypeDefaultDescription
history'push' | 'replace''push'Determines how the history stack is updated. 'push' adds a new entry, 'replace' replaces the current one.
info.scrollToTopbooleantrueWhether to scroll to the top of the page after navigation.
info.scrollBehavior'instant' | 'smooth' | 'auto''instant'How the scroll happens when scrollToTop is true.

Usage Examples

Basic navigation
import { navigate } from "rwsdk/client";

navigate("/about");
Navigation with replace
import { navigate } from "rwsdk/client";

navigate("/profile", { history: "replace" });
Navigation with smooth scroll
import { navigate } from "rwsdk/client";

navigate("/dashboard", {
  info: {
    scrollBehavior: "smooth",
  },
});