Zero-config setup
Works out of the box with the default src/assets layout. No configuration required for standard React Native projects.
Stop shipping string-based asset paths. Generate the registry your React Native project can actually type-check.
Scan the asset tree, emit stable TypeScript constants and a manifest, then find unused files or rewrite stale references through explicit commands. The package ships no runtime dependencies and takes TypeScript as its only peer.
Brittle input
// Before: brittle string-based paths
<Image source={require('../../../assets/toast/info.png')} />
<LottieView source={require('../../utils/loading.json')} />Typed output
// After: typed named constants
import { Assets, Lotties } from './generated/assets.gen';
<Image source={Assets.toast.info} />
<LottieView source={Lotties.loading} />Feature surface / 10
Each entry below maps to a command, a flag, or a file the CLI writes.
Works out of the box with the default src/assets layout. No configuration required for standard React Native projects.
Native support for images (PNG/JPG/WebP), SVGs, and Lottie animations (JSON). Each type gets its own typed export.
Generated files are stable across runs and friendly to code review. Keys are sorted alphabetically for consistent diffs.
Files that normalize to the same key (harini-cry.png and harini_cry.png, for example) are caught at generation time with clear errors.
The audit command compares the manifest against the scanned source roots, reports every unreferenced asset, and deletes them on request.
The generate --inplace command rewrites every require() call and stale dotted reference in the project's source files.
Each manifest entry carries a SHA-1 hash, enabling the codemod to track files that move or are renamed without content changes.
The organize command migrates flat or legacy asset directories into canonical subdirectories (images/, svgs/, lotties/).
Override paths, export names, TypeScript type imports, or add entirely new asset types through rn-typed-assets.config.js.
Every function is exported, so the generation pipeline can run from a build script instead of the CLI.
Generation pipeline / 04
The same asset directory produces the same registry on every run, which is what makes the output reviewable in a diff.
For each enabled asset type, recursively list files under the configured rootDir.
Convert each filename to a stable camelCase key, resolving numeric prefixes and collisions.
Assemble a nested object tree from path segments, detecting and resolving branch and leaf collisions.
Write assets.gen.ts as a typed const object and assets.manifest.json as a stable index of every key to file mapping.
Example asset tree
src/assets/
toast/
info.png
warning.png
coupang/
harini-cry.png
lottie/
loading.json
svg/
logo.svgFilenames become identifiers through fixed rules rather than per-project conventions, so two developers regenerating the same tree get the same keys.
| Filename | Generated key |
|---|---|
| harini-cry.png | hariniCry |
| camera_guide.png | cameraGuide |
| Info-Filled.png | infoFilled |
| 1.png | n1 (numeric prefix → n) |
| point.png alongside point/ dir | pointAsset (collision) |
Keys are sorted, types are imported per asset type, and the whole object is const so TypeScript narrows every reference.
1// src/generated/assets.gen.ts2import type { ImageRequireSource } from 'react-native';3import type { AnimationObject } from 'lottie-react-native';4export type SvgsAssetSource = unknown;5 6export const Assets = {7 coupang: {8 hariniCry: require('../assets/coupang/harini-cry.png') as ImageRequireSource,9 },10 toast: {11 info: require('../assets/toast/info.png') as ImageRequireSource,12 warning: require('../assets/toast/warning.png') as ImageRequireSource,13 },14} as const;15 16export const Lotties = {17 loading: require('../assets/lottie/loading.json') as AnimationObject,18} as const;19 20export const Svgs = {21 logo: require('../assets/svg/logo.svg') as SvgsAssetSource,22} as const;Quick start / 05
Install as a dev dependency, run the generator, then replace require() paths with named constants.
What the registry changes
Add rn-typed-assets as a dev dependency. The project needs TypeScript available; most React Native projects already have it.
npm install --save-dev rn-typed-assetsDeclare the generation, organization, and audit commands once as npm scripts.
{
"scripts": {
"assets:generate": "rn-typed-assets generate",
"assets:generate:inplace": "rn-typed-assets generate --inplace",
"assets:organize": "rn-typed-assets organize src/assets",
"assets:audit": "rn-typed-assets audit",
"assets:audit:fix": "rn-typed-assets audit --fix"
}
}Execute the generate command to create assets.gen.ts and assets.manifest.json in src/generated/.
npm run assets:generateImport the typed registry and use named constants instead of require() paths.
import { Assets, Lotties, Svgs } from './generated/assets.gen';
// Images
<Image source={Assets.toast.info} />
<Image source={Assets.coupang.hariniCry} />
// Lottie animations
<LottieView source={Lotties.loading} autoPlay loop />
// SVGs (with react-native-svg)
<SvgUri source={Svgs.logo} />Run the audit to list unreferenced assets, then delete them in the same command.
npm run assets:audit # report unused entries
npm run assets:audit -- --fix # delete unused files and regenerateCLI reference / 03
Three commands cover the whole surface. Each one lists its options and the default it falls back to when the flag is absent.
Scan asset directories and emit assets.gen.ts + assets.manifest.json.
| Flag | Description | Default |
|---|---|---|
| --types <types> | Comma-separated asset types | image,svg,lottie |
| --inplace | Rewrite source files after regen | false |
| --root <path> | Project root directory | cwd |
| --config <path> | Path to config file | ./rn-typed-assets.config.js |
Examples
rn-typed-assets generate
rn-typed-assets generate --inplace
rn-typed-assets generate --types=image,lottieMove asset files into canonical subdirectories, then regenerate and rewrite sources.
Creates: images/, svgs/, lotties/ subdirectories
| Flag | Description | Default |
|---|---|---|
| <assetsDir> | Path to the asset root to organize | (required) |
| --types <types> | Asset types to move | image,svg,lottie |
Examples
rn-typed-assets organize src/assetsCompare manifest against actual source-file usages.
| Flag | Description | Default |
|---|---|---|
| --types <types> | Asset types to audit | image,svg,lottie |
| --fix | Delete unused files and regenerate | false |
Examples
rn-typed-assets audit
rn-typed-assets audit --fix
rn-typed-assets audit --types=imageConfiguration / optional
The defaults cover the standard layout. A config file at the project root replaces only the fields it names, so omitting it accepts every default.
Every field below is an override. The commented value is what the CLI uses when the field is absent.
1// rn-typed-assets.config.js2module.exports = {3 // Where to write assets.gen.ts and assets.manifest.json4 // Default: 'src/generated'5 outputDir: 'src/generated',6 7 // Directories and entry files scanned by the audit command8 // Default: ['src', 'App.tsx', 'index.js']9 sourceRoots: ['src', 'App.tsx', 'index.js'],10 11 // Per-type configuration (all fields are optional overrides)12 types: {13 image: {14 rootDir: 'src/assets', // scan root15 extensions: ['.png', '.jpg', '.jpeg', '.webp'],16 exportName: 'Assets', // export const Assets = ...17 typeImport: {18 typeName: 'ImageRequireSource', // TypeScript type name19 from: 'react-native', // import source20 },21 },22 svg: {23 rootDir: 'src/assets/svg',24 extensions: ['.svg'],25 exportName: 'Svgs',26 inlineType: 'unknown', // emits: export type SvgsAssetSource = unknown27 },28 lottie: {29 rootDir: 'src/assets/lottie',30 extensions: ['.json'],31 exportName: 'Lotties',32 typeImport: {33 typeName: 'AnimationObject',34 from: 'lottie-react-native',35 },36 },37 },38};Any type outside the three defaults can be declared under types. The audit command discovers it automatically through the exportName to type reverse map.
// Adding a custom font asset type
module.exports = {
types: {
font: {
rootDir: 'src/assets/fonts',
extensions: ['.ttf', '.otf'],
exportName: 'Fonts',
inlineType: 'string', // emits: export type FontsAssetSource = string
},
},
};Configuration options
CI integration / 02
Regenerate in CI and diff the output, or regenerate on commit and stage it. Both catch the same drift between the manifest and the filesystem.
Regenerates the registry in CI and fails the job when the committed output differs from what the current asset tree produces.
# .github/workflows/ci.yml
- name: Verify asset manifest is up to date
run: |
npm run assets:generate
git diff --exit-code src/generated/Regenerates and stages the registry on every commit through a Husky git hook, so the manifest never lags the filesystem.
# .husky/pre-commit
npm run assets:generate
git add src/generated/assets.gen.ts src/generated/assets.manifest.jsonAsset pipeline / ready
Install the generator as a development dependency, generate the registry, and let TypeScript carry the asset names from there.