{"record":{"id":"f64d40a1a05d8124","repo":"remix-run/react-router","slug":"react-router-presets-must-have-a-name-property-d","errorCode":null,"errorMessage":"React Router presets must have a `name` property defined.","messagePattern":"React Router presets must have a `name` property defined\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-router-dev/config/config.ts","lineNumber":494,"sourceCode":"      if (validateConfig) {\n        const error = validateConfig(reactRouterUserConfig);\n        if (error) {\n          return err(error);\n        }\n      }\n    } catch (error) {\n      return err(`Error loading ${reactRouterConfigFile}: ${error}`);\n    }\n  }\n\n  // Prevent mutations to the user config\n  reactRouterUserConfig = deepFreeze(cloneDeep(reactRouterUserConfig));\n\n  let presets: ReactRouterConfig[] = (\n    await Promise.all(\n      (reactRouterUserConfig.presets ?? []).map(async (preset) => {\n        if (!preset.name) {\n          throw new Error(\n            \"React Router presets must have a `name` property defined.\",\n          );\n        }\n\n        if (!preset.reactRouterConfig) {\n          return null;\n        }\n\n        let configPreset: ReactRouterConfig = omit(\n          await preset.reactRouterConfig({ reactRouterUserConfig }),\n          excludedConfigPresetKeys,\n        );\n\n        return configPreset;\n      }),\n    )\n  ).filter(function isNotNull<T>(value: T | null): value is T {\n    return value !== null;","sourceCodeStart":476,"sourceCodeEnd":512,"githubUrl":"https://github.com/remix-run/react-router/blob/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/packages/react-router-dev/config/config.ts#L476-L512","documentation":"Thrown during React Router config resolution when iterating reactRouterUserConfig.presets and a preset object lacks a name property (preset.name is falsy). Presets are an array in react-router.config.ts; each must declare a name for identification and deduplication. The check runs after the user config is loaded, validated, and deep-frozen, so it fires during build/dev startup before the preset's reactRouterConfig callback is invoked.","triggerScenarios":"Authoring react-router.config.ts with presets: [{ reactRouterConfig: () => ({...}) }] (name omitted); spreading an object that drops name; a preset factory returning an object without name; importing a preset from a package whose default export is unnamed.","commonSituations":"Converting a flat config into presets and forgetting the name field; copy-pasting a preset example that omitted name; upgrading a preset package whose API changed to require name; community preset without a name.","solutions":["Add a unique name string to every preset in react-router.config.ts: presets: [{ name: 'my-preset', reactRouterConfig: () => ({...}) }].","If importing a preset from a package, ensure its exported object includes name, or wrap it: { name: 'pkg', ...importedPreset }.","Remove the offending preset if it is unused.","Lint your config by logging presets.map(p => p.name) before build to catch missing names early."],"exampleFix":"// before (react-router.config.ts)\nexport default {\n  presets: [{ reactRouterConfig: () => ({ ssr: true }) }],\n} satisfies ReactRouterConfig;\n// after\nexport default {\n  presets: [{ name: 'ssr-preset', reactRouterConfig: () => ({ ssr: true }) }],\n} satisfies ReactRouterConfig;","handlingStrategy":"validation","validationCode":"// Validate presets before build/dev\nfunction presetsAreNamed(presets: any[]): boolean {\n  return Array.isArray(presets) && presets.every((p) => typeof p?.name === 'string' && p.name.length > 0);\n}\nconst cfg = (await import('./react-router.config.ts')).default;\nif (!presetsAreNamed(cfg.presets ?? [])) {\n  throw new Error('Every preset must define a unique `name`.');\n}","typeGuard":"interface ReactRouterPreset { name: string; reactRouterConfig?: (ctx: any) => any | Promise<any>; }\nfunction isNamedPreset(p: unknown): p is ReactRouterPreset {\n  return typeof p === 'object' && p !== null && typeof (p as any).name === 'string' && (p as any).name.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Always specify name when authoring presets in react-router.config.ts.","Add a config lint step to your CI that asserts every preset has a name.","When wrapping imported presets, spread them after setting name: { name: 'pkg', ...imported }.","Document the name requirement in your preset package's README."],"tags":["config","preset","react-router-dev","framework-mode"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}