Remix Utilities

9.3.1 · active · verified Sun Apr 19

remix-utils is a comprehensive collection of modular utility functions designed to extend the capabilities of applications built with the Remix framework and React Router. As of version 9.3.1, this library offers a wide array of tools covering both server-side and client-side concerns. Its utilities range from advanced data loading patterns like `promiseHash` for concurrent fetches and `timeout` for resilient API calls, to server-side middleware for handling common web challenges such as client IP detection, CORS, CSRF protection, request ID generation, and server timing. Client-side enhancements include functions like `cacheAssets` for efficient browser caching of build artifacts. The package maintains an `active` and consistent release cadence, frequently pushing out minor and patch updates to introduce new features, resolve bugs, and ensure seamless compatibility with the evolving Remix and React Router ecosystems. A key distinguishing factor is its highly modular design, which allows developers to selectively install and import only the specific utilities needed, thereby keeping bundles lean. Many components can even be installed via a shadcn-like registry for simplified management. This approach differentiates it from monolithic utility libraries, promoting a more focused and performant integration within Remix projects.

Common errors

Warnings

Install

Imports

Quickstart

This example demonstrates using `promiseHash` to concurrently fetch multiple data sources in a Remix loader and applies `timeout` to an external API call to prevent long-running requests from blocking the response, handling potential `TimeoutError`s gracefully.

import { json } from "@remix-run/node";
import { promiseHash, timeout, TimeoutError } from "remix-utils/promise";

// Mock API calls
async function getUser(request: Request): Promise<{ id: string; name: string }> {
  await new Promise(resolve => setTimeout(resolve, Math.random() * 50));
  return { id: "user-123", name: "John Doe" };
}

async function getPosts(request: Request): Promise<{ id: string; title: string }[]> {
  await new Promise(resolve => setTimeout(resolve, Math.random() * 150));
  return [{ id: "post-a", title: "My First Post" }, { id: "post-b", title: "Another One" }];
}

// A potentially slow external service
async function getExternalData(request: Request): Promise<string> {
  await new Promise(resolve => setTimeout(resolve, 200)); // Simulating a slow API
  return "External data loaded!";
}

export async function loader({ request }: { request: Request }) {
  try {
    const { user, posts, externalData } = await promiseHash({
      user: getUser(request),
      posts: getPosts(request),
      // Attach a timeout to a potentially slow external call
      externalData: timeout(getExternalData(request), { ms: 100, message: "External data timed out" })
    });

    return json({ user, posts, externalData });
  } catch (error) {
    if (error instanceof TimeoutError) {
      console.error("Loader timeout error:", error.message);
      return json({ error: "One or more external services timed out. Please try again." }, { status: 504 });
    }
    console.error("Loader error:", error);
    return json({ error: "An unexpected error occurred." }, { status: 500 });
  }
}

view raw JSON →