Tips from 8 months of TanStack/Router in production

On my last day at Tia I wrote a master vision doc for our TanStack Router app. Here are the parts I can share.

These are battle-tested patterns and lessons learned from using TanStack Router in production since before v1. They worked well for us to iterate quickly, work as a team, and have shockingly few bugs. I can't know how they'll evolve in the future.

TanStack Router 101

If you need a refresher, TanStack Router is the modern React for the rest of us. It combines the best parts of Remix and NextJS for a wonderful type-safe routing experience with all the features you expect:

UX vision for the app

Our app is based on the principles of multi-page apps. An important nuance makes it different to the multi page apps of yore – we aim to use a unique URL for every significant UI state, but we avoid full-page reloads and round-trips to the server.

This is where TanStack Router shines.

Goals:

We're building for power users: they don't need us to hold their hands, they need us to get out of the way. This is the mental image I like to use for what we're building 👇

Guiding principles:

Code organization

The app follows a file-based routing setup from TanStack Router.

That means the file structure closely follows the URL structure, which makes things easy to find and fairly easy to collocate.

We try to organize code into vertical modules or components – every page folder should contain everything it needs to work. And every component or function should live at the nearest shared space in the hierarchy.

../../../ imports are the main code smell to look out for.

Horizontal concerns

We've identified a few horizontal concerns like aspects of loading shared data and certain UI atoms that don't belong to any particular page.

We try to treat those like libraries.

There is a shared /data/ and /pages/-components directory for these which have a path alias for easier imports. This smells a lot like a custom SDK or ORM + component library and should be split off when it matures. Avoid leaking page-specific concerns into these.

Data patterns

TanStack Router supports data loaders at the router level and implements first-party Suspense support.

In practice that means your components don't need to deal with their own loading and error states. You can build your components to focus on the happy path, then let the router handle loading and error states.

A few rules of thumb we’ve developed over time:

Go gardening

We like to take a sit-back-and-observe approach. Avoid generalizing code until you've been repeating the same thing and it def looks like a pattern. This makes it easier to keep code flexible and making the abstractions we do build feeling more natural.

Hope this helps. It's a pile of app design guidelines I plan to keep in the next thing I touch.

Cheers,
~Swizec