{"id":11936,"library":"remix-utils","title":"Remix Utilities","description":"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.","status":"active","version":"9.3.1","language":"javascript","source_language":"en","source_url":"https://github.com/sergiodxa/remix-utils","tags":["javascript","client IP address","client locale","client-only","cors","csrf","hydrated","json hash","named action"],"install":[{"cmd":"npm install remix-utils","lang":"bash","label":"npm"},{"cmd":"yarn add remix-utils","lang":"bash","label":"yarn"},{"cmd":"pnpm add remix-utils","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core React dependency for all UI utilities and Remix.","package":"react","optional":false},{"reason":"Core routing dependency for all UI utilities and Remix.","package":"react-router","optional":false},{"reason":"Required for specific batching utilities.","package":"@edgefirst-dev/batcher","optional":true},{"reason":"Required for JWT-related middleware.","package":"@edgefirst-dev/jwt","optional":true},{"reason":"Required for server timing middleware.","package":"@edgefirst-dev/server-timing","optional":true},{"reason":"Required for cryptographic operations, e.g., CSRF middleware.","package":"@oslojs/crypto","optional":true},{"reason":"Required for client IP detection utilities.","package":"is-ip","optional":true},{"reason":"Used for schema validation, especially after removal of Zod-based typed sessions.","package":"@standard-schema/spec","optional":true}],"imports":[{"note":"Many utilities are exposed via subpath exports; `promiseHash` is located under the 'promise' subpath. This is a common pattern for modular libraries to avoid bloating the main bundle.","wrong":"import { promiseHash } from 'remix-utils';","symbol":"promiseHash","correct":"import { promiseHash } from 'remix-utils/promise';"},{"note":"The `timeout` utility and its associated `TimeoutError` are also found in the 'promise' subpath. Always import the error type if you plan to catch specific timeout exceptions.","wrong":"import { timeout } from 'remix-utils';","symbol":"timeout","correct":"import { timeout, TimeoutError } from 'remix-utils/promise';"},{"note":"This function is specifically designed for client-side execution within `entry.client`. Importing it from the root or attempting to run it on the server will result in errors.","wrong":"import { cacheAssets } from 'remix-utils';","symbol":"cacheAssets","correct":"import { cacheAssets } from 'remix-utils/cache-assets';"},{"note":"Utility functions or middleware like `json` are often found under specific subpaths. Always check the documentation or the library's `package.json` exports for the exact import path.","wrong":"import { json } from 'remix-utils';","symbol":"json (middleware helper)","correct":"import { json } from 'remix-utils/json';"}],"quickstart":{"code":"import { json } from \"@remix-run/node\";\nimport { promiseHash, timeout, TimeoutError } from \"remix-utils/promise\";\n\n// Mock API calls\nasync function getUser(request: Request): Promise<{ id: string; name: string }> {\n  await new Promise(resolve => setTimeout(resolve, Math.random() * 50));\n  return { id: \"user-123\", name: \"John Doe\" };\n}\n\nasync function getPosts(request: Request): Promise<{ id: string; title: string }[]> {\n  await new Promise(resolve => setTimeout(resolve, Math.random() * 150));\n  return [{ id: \"post-a\", title: \"My First Post\" }, { id: \"post-b\", title: \"Another One\" }];\n}\n\n// A potentially slow external service\nasync function getExternalData(request: Request): Promise<string> {\n  await new Promise(resolve => setTimeout(resolve, 200)); // Simulating a slow API\n  return \"External data loaded!\";\n}\n\nexport async function loader({ request }: { request: Request }) {\n  try {\n    const { user, posts, externalData } = await promiseHash({\n      user: getUser(request),\n      posts: getPosts(request),\n      // Attach a timeout to a potentially slow external call\n      externalData: timeout(getExternalData(request), { ms: 100, message: \"External data timed out\" })\n    });\n\n    return json({ user, posts, externalData });\n  } catch (error) {\n    if (error instanceof TimeoutError) {\n      console.error(\"Loader timeout error:\", error.message);\n      return json({ error: \"One or more external services timed out. Please try again.\" }, { status: 504 });\n    }\n    console.error(\"Loader error:\", error);\n    return json({ error: \"An unexpected error occurred.\" }, { status: 500 });\n  }\n}","lang":"typescript","description":"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."},"warnings":[{"fix":"Migrate session validation to use `@standard-schema/spec` as introduced in v8.8.0, or implement custom validation logic. Review the v8.x to v9.x upgrade guides.","message":"Version 9.0.0 removed `Typed Session Storage` and its dependency on Zod v3. If your application relied on these features for schema-validated sessions, significant refactoring is required.","severity":"breaking","affected_versions":">=9.0.0"},{"fix":"Always consult the release notes and upgrade guides in the documentation (e.g., `./docs/v6-to-v7.md`) before performing major version upgrades.","message":"Upgrading from v6 to v7 introduced breaking changes that require following a specific upgrade guide. Subsequent major version upgrades (e.g., v8 to v9) also often contain breaking changes due to alignment with Remix and React Router versions.","severity":"breaking","affected_versions":">=7.0.0"},{"fix":"Always refer to the package documentation or the `package.json`'s `exports` field to determine the correct subpath for the specific utility you intend to use.","message":"Many utilities in `remix-utils` are exposed as deeply nested or subpath exports (e.g., `remix-utils/promise`, `remix-utils/cache-assets`). Importing directly from `remix-utils` for these specific utilities will lead to module not found errors.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Check the documentation for each utility you use and ensure all listed optional dependencies (e.g., `@oslojs/crypto` for CSRF, `is-ip` for client IP detection) are explicitly installed in your project.","message":"Several utilities depend on optional peer dependencies that are not automatically installed with `remix-utils`. Forgetting to install these specific dependencies will lead to runtime errors when using the associated features.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Refactor middleware logic to avoid direct mutation of the context. Instead, return new context objects or use other patterns for state management within middleware pipelines.","message":"In v9.0.0, the type for the `middleware` context was marked as read-only. This change impacts middleware functions that attempted to directly mutate the context object.","severity":"breaking","affected_versions":">=9.0.0"},{"fix":"Ensure `cacheAssets` is placed exclusively within your Remix `entry.client.ts` or `entry.client.tsx` file to guarantee it runs in a browser environment.","message":"The `cacheAssets` utility is strictly client-side and must only be executed within your `entry.client` file. Attempting to call it in a server-side context (e.g., a loader or action) will result in a runtime error indicating a non-browser environment.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Correct the import statement to use the specific subpath, e.g., `import { promiseHash } from 'remix-utils/promise';` or `import { cacheAssets } from 'remix-utils/cache-assets';`.","cause":"Incorrect import path for a specific utility. Many utilities are located under subpath exports, not directly from the root `remix-utils` package.","error":"Error: Cannot find module 'remix-utils/my-utility' from '/path/to/your/app'"},{"fix":"Identify the missing dependency by checking the documentation for the specific utility. Install it using `npm add <dependency-name>`, e.g., `npm add @oslojs/crypto`.","cause":"A required optional peer dependency for the specific utility is missing. For example, the CSRF middleware requires `@oslojs/crypto`.","error":"TypeError: (0 , _remix_utils_csrf.createCSRF) is not a function"},{"fix":"Refactor your session management. For typed sessions, consider migrating to patterns supported by `@standard-schema/spec` as introduced in v8.8.0, or use standard Remix session storage.","cause":"Attempting to use `Typed Session Storage` or related Zod-based features after upgrading to `remix-utils` v9.0.0 or later, where these features were removed.","error":"ReferenceError: Zod is not defined"},{"fix":"Move the `cacheAssets()` call exclusively into your `app/entry.client.ts` or `app/entry.client.tsx` file.","cause":"The `cacheAssets` utility was called in a server-side context (e.g., a Remix loader, action, or `entry.server`).","error":"Invariant failed: Expected a browser environment"}],"ecosystem":"npm"}