Skip to content

CLI commands

rndl typegen

Generate typed deep links from your route table, so a wrong route or param fails tsc instead of shipping a broken link.

rndl typegen writes a TypeScript module into your app with a compile-time-checked buildDeepLink and a typed useTypedParams hook, generated from your route table.

rndl typegen --out src/deeplinks.gen.ts --app-dir src/app                          # Expo Router
rndl typegen --out src/deeplinks.gen.ts --config src/navigation/linking.ts#linking # React Navigation
rndl typegen --out src/deeplinks.gen.ts --app-dir src/app --watch                  # regenerate on change

What you get

Given a route like /users/[id] (Expo Router) or /users/:id (React Navigation), the generated module exports:

import { buildDeepLink, useTypedParams } from './deeplinks.gen';
 
// Build a link. The route and its params are checked at compile time.
const url = buildDeepLink('/users/[id]', { id: '42' });
// => 'myapp://users/42'
 
// Override the baked-in scheme when you need a universal link.
const web = buildDeepLink('/users/[id]', { id: '42' }, 'https://example.com');
// => 'https://example.com/users/42'
 
// Read the current screen's params, typed for the route.
const { id } = useTypedParams<'/users/[id]'>();
 
buildDeepLink('/users/[id]', {}); // compile error: missing 'id'

A wrong route key, a missing required param, or a mistyped param value fails tsc, so a broken deep link is caught before it ships.

Under the hood the module declares two interfaces keyed by route: DeepLinkRoutes (the params buildDeepLink accepts; all values are strings because they go into the URL) and DeepLinkParams (what useTypedParams returns, matching what the router actually hands your screen).

Semantics worth knowing

  • Route keys are router-native: Expo Router uses the bracket form you author (/users/[id], /posts/[...slug]); React Navigation keeps its colon patterns (/users/:id).
  • Catch-all params are one a/b/c string when you build a link, and a string[] when you read them back (matching what the router returns).
  • React Navigation params defined with a custom parse function are typed unknown, since their runtime type is not knowable from the config.
  • The scheme baked into the module comes from --scheme, or by default from your app.json scheme or linking prefixes. Pass a prefix as the third buildDeepLink argument to override it per call.
  • The generated module imports its small runtime from @deeplink-devtools/core, so add that as a dependency of your app:
npm install @deeplink-devtools/core

Flags

FlagDescription
--out <file>Required. Where to write the generated module.
--app-dir <dir>Expo Router app directory to generate from.
--config <module[#export]>React Navigation linking module to generate from.
--scheme <scheme>Scheme or prefix baked into buildDeepLink.
--watchWatch the route source and regenerate on change.

To keep the types honest in CI, commit the generated file and run tsc over a file that exercises it; a route change that invalidates a call site then fails the build instead of a user's tap.