Remix Utils 
This package contains simple utility functions to use with 
Remix.run .
Installation 
` ` ` bash npm   install   remix - utils ` ` ` 
API Reference 
promiseHash 
The promiseHash  function is not directly related to Remix but it's a useful function when working with loaders and actions.
This function is an object version of Promise.all  which lets you pass an object with promises and get an object with the same keys with the resolved values.
` ` ` ts export   async   function   loader ( {   request   } :   LoaderArgs )   {    return   json (      await   promiseHash ( {        user :   getUser ( request ) ,        posts :   getPosts ( request ) ,      } )    ) ; } ` ` ` 
You can use nested promiseHash  to get a nested object with resolved values.
` ` ` ts export   async   function   loader ( {   request   } :   LoaderArgs )   {    return   json (      await   promiseHash ( {        user :   getUser ( request ) ,        posts :   promiseHash ( {          list :   getPosts ( request ) ,          comments :   promiseHash ( {            list :   getComments ( request ) ,            likes :   getLikes ( request ) ,          } ) ,        } ) ,      } )    ) ; } ` ` ` 
timeout 
The timeout  function lets you attach a timeout to any promise, if the promise doesn't resolve or reject before the timeout, it will reject with a TimeoutError .
` ` ` ts try   {    let   result   =   await   timeout ( fetch ( "https://example.com" ) ,   {   ms :   100   } ) ; }   catch   ( error )   {    if   ( error   instanceof   TimeoutError )   {      // Handle timeout    } } ` ` ` 
Here the fetch needs to happen in less than 100ms, otherwise it will throw a TimeoutError .
If the promise is cancellable with an AbortSignal you can pass the AbortController to the timeout  function.
` ` ` ts try   {    let   controller   =   new   AbortController ( ) ;    let   result   =   await   timeout (      fetch ( "https://example.com" ,   {   signal :   controller .signal   } ) ,      {   ms :   100 ,   controller   }    ) ; }   catch   ( error )   {    if   ( error   instanceof   TimeoutError )   {      // Handle timeout    } } ` ` ` 
Here after 100ms, timeout  will call controller.abort()  which will mark the controller.signal  as aborted.
cacheAssets 
This can only be run inside entry.client .
To use it, open your entry.client  file and add this:
` ` ` ts import   {   cacheAssets   }   from   "remix-utils" ; cacheAssets ( ) .catch ( ( error )   =>   {    // do something with the error, or not } ) ; ` ` ` 
The function receives an optional options object with two options:
- 
cacheName  is the name of the 
Cache object  to use, the default value is
assets .
- buildPath  is the pathname prefix for all Remix built assets, the default value is /build/  which is the default build path of Remix itself.
It's important that if you changed your build path in remix.config.js  you pass the same value to cacheAssets  or it will not find your JS files.
The cacheName  can be left as is unless you're adding a Service Worker to your app and want to share the cache.
` ` ` ts cacheAssests ( {   cacheName :   "assets" ,   buildPath :   "/build/"   } ) .catch ( ( error )   =>   {    // do something with the error, or not } ) ; ` ` ` 
ClientOnly 
The ClientOnly component lets you render the children element only on the client-side, avoiding rendering it the server-side.
If you're using React 18 and a streaming server rendering API  (eg. [renderToPipeableStream ](https://beta.reactjs.org/reference/react-dom/server/renderToPipeableStream)) you probably want to use a <Suspense>  boundary instead.
>
export default function Component() {
    <Suspense fallback={<SimplerStaticVersion />}>
      <ComplexComponentNeedingBrowserEnvironment />
>
You can provide a fallback component to be used on SSR, and while optional, it's highly recommended to provide one to avoid content layout shift issues.
` ` ` tsx import   {   ClientOnly   }   from   "remix-utils" ; export   default   function   Component ( )   {    return   (      < ClientOnly   fallback = { < SimplerStaticVersion   / > } >        { ( )   =>   < ComplexComponentNeedingBrowserEnvironment   / > }      < / ClientOnly >    ) ; } ` ` ` 
This component is handy when you have some complex component that needs a browser environment to work, like a chart or a map. This way, you can avoid rendering it server-side and instead use a simpler static version like an SVG or even a loading UI.
The rendering flow will be:
- SSR: Always render the fallback.
- CSR First Render: Always render the fallback.
- CSR Update: Update to render the actual component.
- CSR Future Renders: Always render the actual component, don't bother to render the fallback.
This component uses the useHydrated  hook internally.
ServerOnly 
The ServerOnly component is the opposite of the ClientOnly component, it lets you render the children element only on the server-side, avoiding rendering it the client-side.
You can provide a fallback component to be used on CSR, and while optional, it's highly recommended to provide one to avoid content layout shift issues, unless you only render visually hidden elements.
` ` ` tsx import   {   ServerOnly   }   from   "remix-utils" ; export   default   function   Component ( )   {    return   (      < ServerOnly   fallback = { < ComplexComponentNeedingBrowserEnvironment   / > } >        { ( )   =>   < SimplerStaticVersion   / > }      < / ServerOnly >    ) ; } ` ` ` 
This component is handy to render some content only on the server-side, like a hidden input you can later use to know if JS has loaded.
Consider it like the `
` HTML tag but it can work even if JS failed to load but it's enabled on the browser.
The rendering flow will be:
- SSR: Always render the children.
- CSR First Render: Always render the children.
- CSR Update: Update to render the fallback component (if defined).
- CSR Future Renders: Always render the fallback component, don't bother to render the children.
This component uses the useHydrated  hook internally.
CORS 
The CORS function let you implement CORS headers on your loaders and actions so you can use them as an API for other client-side applications.
There are two main ways to use the cors  function.
1. Use it on each loader/action where you want to enable it.
2. Use it globally on entry.server handleRequest and handleDataRequest export.
If you want to use it on every loader/action, you can do it like this:
` ` ` ts export   async   function   loader ( {   request   } :   LoaderArgs )   {    let   data   =   await   getData ( request ) ;    let   response   =   json < LoaderData > ( data ) ;    return   await   cors ( request ,   response ) ; } ` ` ` 
You could also do the json  and cors  call in one line.
` ` ` ts export   async   function   loader ( {   request   } :   LoaderArgs )   {    let   data   =   await   getData ( request ) ;    return   await   cors ( request ,   json < LoaderData > ( data ) ) ; } ` ` ` 
And because cors  mutates the response, you can also call it and later return.
` ` ` ts export   async   function   loader ( {   request   } :   LoaderArgs )   {    let   data   =   await   getData ( request ) ;    let   response   =   json < LoaderData > ( data ) ;    await   cors ( request ,   response ) ;   // this mutates the Response object    return   response ;   // so you can return it here } ` ` ` 
If you want to setup it globally once, you can do it like this in entry.server 
` ` ` tsx const   ABORT_DELAY   =   5000 ; export   default   function   handleRequest (    request :   Request ,    responseStatusCode :   number ,    responseHeaders :   Headers ,    remixContext :   EntryContext )   {    let   callbackName   =   isbot ( request .headers .get ( "user-agent" ) )      ?   "onAllReady"      :   "onShellReady" ;    return   new   Promise ( ( resolve ,   reject )   =>   {      let   didError   =   false ;      let   {   pipe ,   abort   }   =   renderToPipeableStream (        < RemixServer   context = { remixContext }   url = { request .url }   / > ,        {          [ callbackName ] :   ( )   =>   {            let   body   =   new   PassThrough ( ) ;            responseHeaders .set ( "Content-Type" ,   "text/html" ) ;            cors (              request ,              new   Response ( body ,   {                headers :   responseHeaders ,                status :   didError   ?   500   :   responseStatusCode ,              } )            ) .then ( ( response )   =>   {              resolve ( response ) ;            } ) ;            pipe ( body ) ;          } ,          onShellError :   ( err :   unknown )   =>   {            reject ( err ) ;          } ,          onError :   ( error :   unknown )   =>   {            didError   =   true ;            console .error ( error ) ;          } ,        }      ) ;      setTimeout ( abort ,   ABORT_DELAY ) ;    } ) ; } export   let   handleDataRequest :   HandleDataRequestFunction   =   async   (    response ,    {   request   } )   =>   {    return   await   cors ( request ,   response ) ; } ; ` ` ` 
Options 
Additionally, the cors  function accepts a options  object as a third optional argument. These are the options.
- origin : Configures the Access-Control-Allow-Origin  CORS header.
  Possible values are:
  - true : Enable CORS for any origin (same as "\*")
  - false : Don't setup CORS
  - string : Set to a specific origin, if set to "\*" it will allow any origin
  - RegExp : Set to a RegExp to match against the origin
  - `Array`: Set to an array of origins to match against the    string or RegExp
  - Function : Set to a function that will be called with the request origin
    and should return a boolean indicating if the origin is allowed or not.
    The default value is true .
- methods : Configures the Access-Control-Allow-Methods  CORS header.
  The default value is ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"] .
- allowedHeaders : Configures the Access-Control-Allow-Headers  CORS header.
- exposedHeaders : Configures the Access-Control-Expose-Headers  CORS header.
- credentials : Configures the Access-Control-Allow-Credentials  CORS header.
- maxAge : Configures the Access-Control-Max-Age  CORS header.
CSRF 
The CSRF related functions let you implement CSRF protection on your application.
This part of Remix Utils needs React and server-side code.
Generate the authenticity token 
In the server, we need to add to our root  component the following.
` ` ` ts import   {   createAuthenticityToken ,   json   }   from   "remix-utils" ; import   {   getSession ,   commitSession   }   from   "~/services/session.server" ; interface   LoaderData   {    csrf :   string ; } export   async   function   loader ( {   request   } :   LoaderArgs )   {    let   session   =   await   getSession ( request .headers .get ( "cookie" ) ) ;    let   token   =   createAuthenticityToken ( session ) ;    return   json < LoaderData > (      {   csrf :   token   } ,      {   headers :   {   "Set-Cookie" :   await   commitSession ( session )   }   }    ) ; } ` ` ` 
The createAuthenticityToken  function receives a session object and stores the authenticity token there using the csrf  key (you can pass the key name as a second argument). Finally, you need to return the token in a json  response and commit the session.
Render the AuthenticityTokenProvider 
You need to read the authenticity token and render the AuthenticityTokenProvider  component wrapping your code in your root.
` ` ` tsx import   {   Outlet ,   useLoaderData   }   from   "@remix-run/react" ; import   {   Document   }   from   "~/components/document" ; export   default   function   Component ( )   {    let   {   csrf   }   =   useLoaderData < LoaderData > ( ) ;    return   (      < AuthenticityTokenProvider   token = { csrf } >        < Document >          < Outlet   / >        < / Document >      < / AuthenticityTokenProvider >    ) ; } ` ` ` 
With this, your whole app can access the authenticity token generated in the root.
Rendering a Form 
When you create a form in some route, you can use the AuthenticityTokenInput  component to add the authenticity token to the form.
` ` ` tsx import   {   Form   }   from   "@remix-run/react" ; import   {   AuthenticityTokenInput   }   from   "remix-utils" ; export   default   function   Component ( )   {    return   (      < Form   method = "post" >        < AuthenticityTokenInput   / >        < input   type = "text"   name = "something"   / >      < / Form >    ) ; } ` ` ` 
Note that the authenticity token is only really needed for a form that mutates the data somehow. If you have a search form making a GET request, you don't need to add the authenticity token there.
This AuthenticityTokenInput  will get the authenticity token from the AuthenticityTokenProvider  component and add it to the form as the value of a hidden input with the name csrf . You can customize the field name using the name  prop.
` ` ` tsx < AuthenticityTokenInput   name = "customName"   / > ` ` ` 
You should only customize the name if you also changed it on createAuthenticityToken .
Alternative: Using useAuthenticityToken  and useFetcher . 
If you need to use useFetcher  (or useSubmit ) instead of Form  you can also get the authenticity token with the useAuthenticityToken  hook.
` ` ` tsx import   {   useFetcher   }   from   "remix" ; import   {   useAuthenticityToken   }   from   "remix-utils" ; export   function   useMarkAsRead ( )   {    let   fetcher   =   useFetcher ( ) ;    let   csrf   =   useAuthenticityToken ( ) ;    return   function   submit ( data )   {      fetcher .submit ( {   csrf ,   . . .data   } ,   {   action :   "/action" ,   method :   "post"   } ) ;    } ; } ` ` ` 
Verify in the Action 
Finally, you need to verify the authenticity token in the action that received the request.
` ` ` ts import   {   verifyAuthenticityToken ,   redirectBack   }   from   "remix-utils" ; import   {   getSession ,   commitSession   }   from   "~/services/session.server" ; export   async   function   action ( {   request   } :   ActionArgs )   {    let   session   =   await   getSession ( request .headers .get ( "Cookie" ) ) ;    await   verifyAuthenticityToken ( request ,   session ) ;    // do something here    return   redirectBack ( request ,   {   fallback :   "/fallback"   } ) ; } ` ` ` 
Suppose the authenticity token is missing on the session, the request body, or doesn't match. In that case, the function will throw an Unprocessable Entity response that you can either catch and handle manually or let pass and render your CatchBoundary.
DynamicLinks 
Warning : Deprecated in favor of the V2_MetaFunction . This will be removed in the next major version. Check below for the new way to do this.
If you need to create ` ` tags based on the loader data instead of being static, you can use the `DynamicLinks` component together with the `DynamicLinksFunction` type.
In the route you want to define dynamic links add handle  export with a dynamicLinks  method, this method should implement the DynamicLinksFunction  type.
` ` ` ts // create the dynamicLinks function with the correct type // note: loader type is optional let   dynamicLinks :   DynamicLinksFunction < SerializeFrom < typeof   loader >>   =   ( {    id ,    data ,    params ,    matches ,    location ,    parentsData , } )   =>   {    if   ( ! data .user )   return   [] ;    return   [ {   rel :   "preload" ,   href :   data .user .avatar ,   as :   "image"   } ] ; } ; // and export it through the handle, you could also create it inline here // if you don't care about the type export   let   handle   =   {   dynamicLinks   } ; ` ` ` 
Then, in the root route, add the DynamicLinks  component before the Remix's Links component, usually inside a Document component.
` ` ` tsx import   {   Links ,   LiveReload ,   Meta ,   Scripts ,   ScrollRestoration   }   from   "remix" ; import   {   DynamicLinks   }   from   "remix-utils" ; type   Props   =   {   children :   React .ReactNode ;   title ? :   string   } ; export   function   Document ( {   children ,   title   } :   Props )   {    return   (      < html   lang = "en" >        < head >          < meta   charSet = "utf-8"   / >          < meta   name = "viewport"   content = "width=device-width,initial-scale=1"   / >          { title   ?   < title > { title } < / title >   :   null }          < Meta   / >          < DynamicLinks   / >          < Links   / >        < / head >        < body >          { children }          < ScrollRestoration   / >          < Scripts   / >          < LiveReload   / >        < / body >      < / html >    ) ; } ` ` ` 
Now, any link you defined in the DynamicLinksFunction  will be added to the HTML as any static link in your LinksFunction s.
You can also put the DynamicLinks  after the Links  component, it's up to you what to prioritize, since static links are probably prefetched when you do <Link prefetch>  you may want to put the DynamicLinks  first to prioritize them.
If you want to upgrade to use the V2_MetaFunction , first enable it in your Remix app:
` ` ` js /** @type {import('@remix-run/ dev ').AppConfig} */ module .exports   =   {    future :   {   v2_meta :   true   } , } ; ` ` ` 
Then you can use it like this:
` ` ` ts export   let   meta :   V2_MetaFunction < typeof   loader >   =   ( {   data   } )   =>   {    if   ( ! data .user )   return   [] ;    return   [      {   tagName :   "link" ,   rel :   "preload" ,   href :   data .user .avatar ,   as :   "image"   } ,    ] ; } ; ` ` `