Skip to content

refactor(atoms)!: 789 — name every route atom *RouteAtom, and only route atoms - #101

Open
randomdevpete wants to merge 1 commit into
task-685-investigate-required-route-typingfrom
task-789-converge-core-atom-naming
Open

refactor(atoms)!: 789 — name every route atom *RouteAtom, and only route atoms#101
randomdevpete wants to merge 1 commit into
task-685-investigate-required-route-typingfrom
task-789-converge-core-atom-naming

Conversation

@randomdevpete

Copy link
Copy Markdown
Owner

Stacked on #100 (task-685-investigate-required-route-typing) — base this PR against that branch, not master.

The rule

The suffix names the return type: an export whose value is a RouteAtom — readable for a
RouteReturn, writable to navigate, usable as another route's parent — ends in RouteAtom;
any other atom ends in Atom alone.

The rule was already half-present, not invented from scratch: it held across every type
(RouteAtom/AsyncRouteAtom carry Route, NavigationGuardAtom correctly doesn't), and for 6
of 11 atom-valued exports plus all 4 correctly-bare non-routes. Five were on the wrong side of the
line, and all five were routes — one-directional drift, not a real second category:

old new
rootAtom rootRouteAtom
createRootAtom createRootRouteAtom
redirectAtom redirectRouteAtom
queryParamAtom queryParamRouteAtom
validateAtom validateRouteAtom
RootOptions RootRouteOptions
QueryParamOptions QueryParamRouteOptions

Unchanged: locationAtom, queryAtom, notAtom, navigationGuardAtom (none of these return a
RouteAtom), and every export that already ended in RouteAtom
(routeAtom, staticRouteAtom, paramRouteAtom, numericRouteAtom, transformRouteAtom,
asyncRouteAtom).

Two rejected rules

Both rival rules that could have justified the status quo fail worse than "drift":

  • "Consumes a path segment"queryParamRouteAtom explicitly doesn't ("Doesn't consume any
    path segments, so path matching continues unaffected"), yet it's a route. rootRouteAtom
    consumes zero segments. redirectRouteAtom swallows whatever's left rather than consuming one.
    Three of the five renamed exports break this rule.
  • "Constructor vs value"rootRouteAtom is a plain value (not a function) and still carries
    Route; notAtom is a factory function and correctly doesn't. Contradicts itself on both
    named-export shapes it's supposed to distinguish.

The pairs that read alike were exactly the misleading ones: rootAtom/locationAtom are both
ambient singletons but only one is a route, and queryAtom/queryParamAtom sound like two
flavours of the same thing when only the second takes part in matching.

Converging the other way (dropping Route from every route atom instead) is fatal:
routeAtomatom collides with jotai's own atom.

No deprecated aliases

Old names are not kept side-by-side with the new ones. Two names per export would leave the
surface less consistent than it started — the opposite of this ticket's goal — and the alias
would need its own removal ticket later. CODING-STYLE.md also bans speculative pre-production
compat hedging; this repo has no stable major yet (major bumps are suppressed, see below), so
there's nothing to be compatible with.

Release shape

Breaking, but ships as 2.7.0, not 3.0.0: .releaserc.json maps breaking: true to a minor
release, and major bumps are deliberately suppressed in this repo. This lands in the same release
train as #98's (675) own BREAKING CHANGE — two breaking renames in one minor release, which is
consistent with how this repo has already chosen to version, not a new precedent.

Known gap — not fixed here

e2e/registry-smoke/ still references resolvedAtom (removed by #98/675) and isn't touched by
this stack either — it installs from the npm registry at latest and its CI job only runs on
manual dispatch, never on PRs, so nothing here regresses it further. It already breaks on the
first release after this stack lands, tracked as ticket 823.

Verification

npm run build (including the docs SSG prerender of all 130 routes), npm test (114 + 42 = 156
tests), npm run typecheck and npm run lint all clean. Confirmed dist/index.d.ts exports only
the five new names and none of the old.

No style-guide exceptions in this diff.

@randomdevpete

Copy link
Copy Markdown
Owner Author

If anything, ending with "atom" is the pattern supported by jotai core for "factory function that returns an atom instance. I think it is therefore more consistent to rename rootRouteAtom (the singleton) to just rootRoute, and createRootRouteAtom (a create prefix being otherwise never seen) renamed to the slightly more palatable rootRouteAtom.

…route atoms

The suffix now names the return type. An export whose value is a `RouteAtom` —
one you can read for a `RouteReturn`, write param values to in order to
navigate, or pass as another route's `parent` — ends in `RouteAtom`; any other
atom ends in `Atom` alone.

Five exports sat on the wrong side of that line, all of them routes. The pairs
that read alike were the misleading ones: `rootAtom` and `locationAtom` are both
ambient singletons but only one is a route, and `queryAtom` and `queryParamAtom`
sound like two flavours of query state when only the second takes part in
matching. The rule already held among the types — `RouteAtom` and
`AsyncRouteAtom` against `NavigationGuardAtom` — so this brings the values into
line with the types rather than inventing a convention, and records it in
DESIGN-NOTES.md so later atoms land on the right side.

Review feedback on the root pair inverted the initial landing: `rootRouteAtom`,
the singleton instance, is not a factory, so per jotai core's own convention —
a `...Atom`-suffixed export is a factory that returns an atom instance, not
the instance itself — the bare instance drops the suffix (`rootRoute`) and the
factory takes it (`createRootRouteAtom` becomes `rootRouteAtom`). This is the
one place in the surface where a default instance and its own factory share a
root word; every other `*RouteAtom` is a pure factory with no competing
instance, so the return-type rule above still decides their names outright.
DESIGN-NOTES.md records both the return-type rule and this narrower exception.

Old names are not kept as deprecated aliases: two names per export would leave
the surface less consistent than it started, which is the opposite of the point.

BREAKING CHANGE: `rootAtom` is renamed `rootRoute`, `createRootAtom` to
`rootRouteAtom`, `redirectAtom` to `redirectRouteAtom`, `queryParamAtom` to
`queryParamRouteAtom`, and `validateAtom` to `validateRouteAtom`. Their
options types follow: `RootOptions` becomes `RootRouteOptions` and
`QueryParamOptions` becomes `QueryParamRouteOptions`. `locationAtom`,
`queryAtom`, `notAtom`, `navigationGuardAtom` and every name already ending in
`RouteAtom` are unchanged.

Ticket: 789
@randomdevpete
randomdevpete force-pushed the task-789-converge-core-atom-naming branch from 20f7896 to 0694402 Compare August 21, 2026 13:18
@randomdevpete randomdevpete reopened this Aug 21, 2026
@randomdevpete

Copy link
Copy Markdown
Owner Author

Done in 0694402

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant