
Product
Socket for Jira Is Now Available
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.
remix-typedjson
Advanced tools
This package is a replacement for superjson to use in your Remix app. It handles a subset of types that `superjson` supports, but is faster and smaller.
This package is a replacement for superjson to use in your Remix app. It handles a subset
of types that superjson supports, but is faster and smaller.
NOTE: Although faster, remix-typedjson is nowhere near as flexible as superjson. It only supports a subset of types with no extensibility. If you need the advanced features of superjson, then I definitely recommend it.
Example site: https://remix-typedjson-example-production.up.railway.app/
Example repo: https://github.com/kiliman/remix-typedjson-example
The following types are supported:
DateBigIntSetMapRegExpundefinedErrorNaNNumber.POSITIVE_INFINITYNumber.NEGATIVE_INFINITYSets and Maps currently only support string keys and JSON serializable values. Complex types coming soon.
In order to get full-type fidelity and type inference, you must be on Remix v1.6.5+. You will also need to import the following replacement functions.
typedjsonnpm i remix-typedjson
Replacement for Remix json helper. It also supports the optional ResponseInit, so you can return headers, etc.
Make sure your loader and action use the new declaration format:
❌ export const loader: LoaderFunction = async ({request}) => {}
❌ export const action: ActionFunction = async ({request}) => {}
✅ export const loader = async ({request}: DataFunctionArgs) => {}
✅ export const action = async ({request}: DataFunctionArgs) => {}
✅ export async function loader({request}: DataFunctionArgs) {}
✅ export async function action({request}: DataFunctionArgs) {}
return typedjson(
{ greeting: 'hello', today: new Date() },
// ResponseInit is optional, just like the `json` helper
{ headers: { 'set-header': await commitSession(session) } },
)
useTypedLoaderDataReplacement for Remix useLoaderData. Use the generic <typeof loader> to
get the correct type inference.
const loaderData = useTypedLoaderData<typeof loader>()
useTypedActionDataReplacement for Remix useActionData. Use the generic <typeof action> to
get the correct type inference.
const actionData = useTypedActionData<typeof action>()
typeddefer✨ New in v0.3.0
Replacement for Remix defer helper. It also supports the optional ResponseInit, so you can return headers, etc.
return typeddefer({
fastData: { message: 'This is fast data', today: new Date() },
slowData: new Promise(resolve => setTimeout(resolve, 2000)).then(() => {
return { message: 'This is slow data', tomorrow: new Date() }
}),
})
<TypedAwait>In your route component, use the new <TypedAwait> component instead of the
Remix <Await> component
export default function DeferRoute() {
const { fastData, slowData } = useTypedLoaderData<typeof loader>()
return (
<main>
<h1>Defer Route</h1>
<h2>Fast Data</h2>
<pre>{JSON.stringify(fastData, null, 2)}</pre>
<div>fastData.today is {fastData.today.toLocaleString()}</div>
<Suspense fallback={<p>Loading slow data...</p>}>
<TypedAwait
resolve={slowData}
errorElement={<p>Error loading slow data!</p>}
>
{slowData => (
<div>
<h2>Slow Data</h2>
<pre>{JSON.stringify(slowData, null, 2)}</pre>
<div>
slowData.tomorrow is {slowData.tomorrow.toLocaleString()}
</div>
</div>
)}
</TypedAwait>
</Suspense>
</main>
)
}
useTypedRouteLoaderDataHelper for useMatches that returns the route data based on provided route id
import { loader as rootLoader } from '~/root'
const rootData = useTypedRouteLoaderData<typeof rootLoader>('root')
useTypedFetcher✨ Add support for key in v0.4.0
Replacement for Remix useFetcher. Use the generic <typeof loader|action> to
get the correct type inference for the fetcher.data property.
const fetcher = useTypedFetcher<typeof action>({ key: 'abc' })
fetcher.data // data property is fully typed
redirectIn order to return a redirect, you will need to import the redirect function from this package, in order for the type inference to work properly.
However, you can also throw redirect() and you can use the original redirect function from Remix.
TypedMetaFunction🔥 Removed in v0.4.0 since it didn't actually work correctly. Will be replaced
with a typedmeta wrapper function in next release
You can now get typed arguments for both data and parentsData from your meta
function export. Based on new feature coming to Remix
export const meta: TypedMetaFunction<typeof loader> = ({ data }) => {
return {
title: `Posts | ${data?.post.title}`,
}
}
// for parentsData, you can specify a Record of typed loaders keyed by route id
// root.tsx
export type LoaderType = typeof loader
// routes/parent.tsx
export type LoaderType = typeof loader
// routes/child.tsx
import { type LoaderType as RootLoaderType } from '~/root'
import { type LoaderType as ParentLoaderType } from '~/routes/parent'
export const meta: TypedMetaFunction<
typeof loader,
// parent loader types keyed by route id
{
'root': RootLoader
'routes/parent': ParentLoader
}
> = ({ data, parentsData }) => {
// access typed parent data by route id
const rootData = parentsData['root']
const parentData = parentsData['routes/parent']
return {
title: `Posts | ${data?.post.title}`,
}
}
registerCustomType✨ New in v0.2.0
remix-typed-json support a limited number of native types in order to keep the
bundle small. However, if you need to support a custom type like Decimal, then
use the registerCustomType API. This way you only pay the cost of the custom
type if you use it.
type CustomTypeEntry<T> = {
type: string
is: (value: unknown) => boolean
serialize: (value: T) => string
deserialize: (value: string) => T
}
export function registerCustomType<T>(entry: CustomTypeEntry<T>)
Register the custom type in root.tsx once.
// root.tsx
import {
typedjson,
registerCustomType,
useTypedLoaderData,
} from 'remix-typedjson'
import Decimal from 'decimal.js'
registerCustomType({
type: 'decimal',
is: (value: unknown) => value instanceof Decimal,
serialize: (value: Decimal) => value.toString(),
deserialize: (value: string) => new Decimal(value),
})
You can now serialize and deserialize the Decimal type.
// route.tsx
export function loader() {
const d = new Decimal('1234567890123456789012345678901234567890')
return typedjson({ greeting: 'Hello World', today: new Date(), d })
}
export default function Index() {
const data = useTypedLoaderData<typeof loader>()
return (
<>
<h2>Loader Data</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
<ul>
<li>today: {data.today.toLocaleString()}</li>
<li>
d instanceof Decimal: {data.d instanceof Decimal ? 'true' : 'false'}
</li>
<li>d: {data.d.toFixed(0)}</li>
</ul>
</>
)
}
Thanks goes to these wonderful people (emoji key):
Kiliman 💻 📖 | Kent C. Dodds 💻 | Simon Knott 💻 🐛 ⚠️ | Tony Truand 💻 ⚠️ | Gregori Rivas 💻 | Afsah Nasir 📖 | Magnus Markling 💻 |
Jozsef Lazar 💻 | Luke Bowerman 💻 | Dan Marshall 📖 | Eric Allam 📖 |
This project follows the all-contributors specification. Contributions of any kind welcome!
FAQs
This package is a replacement for superjson to use in your Remix app. It handles a subset of types that `superjson` supports, but is faster and smaller.
The npm package remix-typedjson receives a total of 45,918 weekly downloads. As such, remix-typedjson popularity was classified as popular.
We found that remix-typedjson demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.