Tanner Linsley - TanStack

Durée: 54m19s

Date de sortie: 31/03/2023

This week we talk to Tanner Linsley, creator of beloved tools like TanStack Query, TanStack Table, and many more. We talk about how he got started in open source, how he approaches building libraries, and what new things he has cooking up. Get a taste of what's to come in TanStack Router and bling.

Become a paid subscriber our patreonspotify, or apple podcasts for the full episode.

TooltipsAndrewJustinTanner

Ce nouveau routier est où j'ai essayé de réécrire l'expérience de routier
du terrain avec des types qui sont invités.
Et ça veut dire que tout de la définition de route,
même les choses de définition neste sont tous types safes.
Et la structure de routier que vous avez, c'est complètement type.
Et puis tous les APIs pour consommer cette structure de routier
doit être typée aussi.
Beaucoup de différents défis dans les différents layers.
Hey, avant de commencer, on veut annoncer des nouvelles manières
que vous pouvez abonner au podcast.
Avec votre subscription, vous avez accès à toutes les versions de la version de la version de notre interview.
Vous pouvez maintenant abonner sur le podcast Apple et Spotify.
Avec ça, on va commencer l'épisode.
Bonjour, bienvenue à l'application de DevTools FM.
C'est un podcast sur les tools de développement.
Je suis Andrew et je suis ma co-host Justin.
Salut tout le monde.
Nous sommes vraiment, vraiment heureux d'avoir Tanner et Lindsay pour nous.
Si vous avez entendu de Tannstack, de React Query,
beaucoup d'autres choses que Tanner a été travaillant.
Tanner, c'est un plaisir.
Je suis vraiment heureux d'avoir parlé avec vous.
Je vous utilise votre software tous les jours.
C'est génial de vous avoir.
Mais avant de nous diviser sur les topics,
Would you like to tell our audience a little bit more about yourself?
Sure thing.
Let's see, I've been doing front-end development
exclusively for about 10 years now.
Programming for about 15.
Got my start in WordPress and PHP.
And then eventually got roped into open source big time.
So over the years, we've really been pouring more and more time into open source,
trying to open source as much as I can,
and tackling the more difficult problems as time goes on.
About eight, nine years ago, I started a company with some friends called Nozzle
that is, it's a search analytics for like marketers and SEO people.
But in a nutshell, we basically reverse engineer pupils' search rankings.
Like everything that comes up on that search page,
we just like pull it out, sticking to a database.
So we're just creating tons of data every day.
And then trying to visualize it.
So building a SaaS product around that,
you can kind of start to see why some of my open source libraries
kind of popped into existence.
Tables, charts, querying, and now a router.
So yeah, there's lots of fun challenges that came up from that.
And just keep sucking me in further.
So got a healthy startup and a lot of healthy open source tools
and things are going great.
Yeah, it's really cool how, as you built out Nozzle,
you open source like seemingly every single part of the front end build pipeline in libraries.
Typically, that's a lot more work than not doing that.
So why did you approach it in that way instead of just building out Nozzle
with only internal stuff?
So we've been really lean from the beginning.
In fact, the most employees we've ever had on the front end team,
including myself, is two.
So for better or worse, I've taken on a lot of responsibility
and worn a lot of hats.
And I have found that one of the only reasons
I've been able to keep up with the demand on the product
is because I've taken the most crucial performance driven parts
of our application, the critical pieces,
and pushed them out to the edge of the community.
So all of our querying, all of our tables,
as much as I can, I push out into this open source layer.
And in a way, I kind of have a bunch of people working for Nozzle,
finding the bugs, making it better,
and just overall helping me have really stable tools
at the core of our product.
So that's part of the reasons why I do like to open source it.
Second of all, I think that just kind of the nature
of a lot of front end technology is just kind of open in general.
It's really difficult to hide front end code
behind intellectual property.
And also there's not a super good alignment of incentives there anyway.
So if you can open source it, I think it's a good choice.
And probably the third reason is that I found that when I started
designing and developing APIs for open source consumption,
it improved the overall quality of my code.
So honestly, even when I'm developing something new inside of Nozzle,
that's kind of a fairly large piece,
I will approach it as if it could someday be open sourced.
And that just helps with a lot of things.
API design and documentation,
you know, making sure it has great typescript support.
If you approach problems with that mindset,
it definitely changes the way that you structure
even things that are internal or brett.
That's really fascinating.
It seems like there would be a few things that fall out of that.
I mean, the very clear benefits, as you said,
is like better documentation.
And you get like all these extra eyes on the code base.
But you also have people who want functionality and features
that your product doesn't necessarily have a need for.
So how do you sort of balance like what are the internal needs
for Nozzle versus like what the community wants
out of a particular solution?
I think the best answer I have to that
is just inversion of control.
If somebody is asking me for a feature
that they can't implement themselves or wrap up into,
you know, some abstraction or a plugin or whatever they want,
I think that's clear sign that the API needs
some better points of inversion.
So, you know, just maybe a couple of years ago,
I had my React Table Library,
but it was really coupled to the DOM, coupled to the markup.
And I saw that people were requesting a lot of features
around markup and can we move things what we want
and can we rip this out and put our own component in.
And it was going to take a lot of time for me
to add those features.
And yes, I didn't need a lot of those things.
But I think that was an indicator
that we just had some bad abstractions.
And once we took the table library,
React Table made it a headless library,
where, you know, we're just going to supply
the logic and the state management
and, you know, the non opinionated markup things about it.
All of those issues and all of those requests
kind of melted away.
And we structured it
so that it had a lot of inversion of control
around the core features.
So now, you have people like Kevin Vandy,
who have built entire table components
and table libraries around the core,
use table hooks.
And that's the power of the right points of abstraction.
So I don't have to worry about any of that stuff now.
Si quelqu'un veut un feature, ils peuvent le construire.
Et si ils suggèrent, et je suis comme,
oh, ça serait bien pour le nozles aussi,
on va travailler ensemble.
Donc je pense que c'est...
Part of it is API design
and part of it is just saying, no.
Hey, that's a great idea.
And I will support you as much as I can
by providing the right API options and abstractions
and points where you can hook into the library.
But beyond that, if it doesn't align with nozzle,
I don't want to have to just say, no, it's impossible.
Mostly, it's like, hey, no, I can't do that,
but it's possible if you want to put in the work.
So that is the balance.
And at least it has been for me.
And it's working out great so far.
Awesome.
Mentioned a TypeScript a little bit in there.
But if I remember correctly,
when you started on your TypeScript journey,
you weren't fully on board.
I remember you having a lot of struggles
at the beginning with trying to type
your very dynamic, large JavaScript libraries.
Can you tell us a little bit about your journey there?
Because I think now you're fully on the TypeScript bandwagon
and understand the benefits.
Absolutely.
So early on, the notion of types was a little jarring at first
because I didn't come from a typed world.
But I did become very familiar with Golang.
A lot of our back in this written in Golang
and all of our back end as-are-go guys.
And it became immediately apparent to me
that types were extremely critical and crucial
for scaling a business and scaling teams and processes
and just having more confidence in your code.
So I wasn't necessarily against the idea of types
from the beginning, but I was just very scared to learn it
because I knew that it was going to detach me
from my momentum for a little while.
So timing was a critical piece for me.
And when you have libraries that are moving quickly
and you're one of two people on a front end team
and you're just kind of moving very quickly there,
it's difficult to sell yourself on,
okay, I'm going to just kind of halt everything I'm doing
and start learning TypeScript.
To make matters even more complicated,
it wasn't that I was just halting work on like our application,
like our SAS product.
Because on the spectrum of consuming application TypeScript
and providing library-level TypeScript,
those are very different things.
I was going to have to do both.
And I didn't necessarily want to do them separate.
So when I learned TypeScript,
I wasn't just consuming it in my SAS product
and using it kind of in a very end user developer way.
But I had to learn it from a very complex context as well
in being able to write really great generics.
I had to learn all about generics
and I had to learn about architecture,
how to build libraries with TypeScript in mind.
And I had to learn about how the compiler worked
and how types flowed through a system.
I had to learn the do's and don'ts of plugin development
and extending TypeScript types,
like system extension in TypeScript.
And some of it was just over my head
because I just had no idea what was going on.
So it took a little bit longer for me to ramp up
because I jumped into the deep end on purpose.
But it was extremely helpful
because now I feel like I'm not scared of generics at all.
I'm not scared of building library code or writing generics.
It doesn't mean I know everything.
There's so many people out there
that know so much more about complex TypeScript.
Now I know who those people are
and I can just say, hey, help me write this.
I can understand it and wrap my head around it eventually.
That's kind of where I ended up and it was harder at first.
So it literally took me probably six months
to get anywhere even remotely useful with my libraries,
especially React Table,
was the library that I started with.
It's a very complex library.
I had lots of plugins and options
and lots of types that permeated the entire system.
So it was difficult.
But once I got it down,
it only took me about another six months to be like,
okay, I think I have a handle on this.
Moving to other libraries
React Query was actually the first one
to get fully moved over with some help from the community.
And to be honest,
React Query is not too complex of a TypeScript project.
There's not a whole lot of types there.
In fact, we mostly just manage about three to four,
maybe five in some places,
generics that kind of flow through the entire system.
As opposed to something like Table and Router
ou anywhere between 9-15,
depending on where you are.
So, yeah, it's been rewarding.
And I knew from day one
that it was going to change
the way that I thought about programming.
It's going to change the kind of developer that I was.
So it was mostly just finding the right time.
And yes, I complained.
And I was like, oh my gosh,
everyone's asking about TypeScript.
And I don't want to build TypeScript right now for my library.
Just leave me alone.
So I think everybody kind of does that
to some point when you're seasoned in the ecosystem
and you need to learn something new.
And you've got a lot of baggage.
It can be hard to jump in.
So my invitation to anybody listening
is that the time to jump in has passed.
You need to get on board.
I still have friends
who are swimming against the current.
And I just don't understand why
because the water is warm.
Yeah. I think where you approach it matters a lot.
Like, if you approach it as an app developer,
like TypeScript seems like just trivial.
Like, oh yeah, that's my project.
But at the library level, it's a totally different game.
And from what it sounds like,
you didn't just jump into the deep end.
You jumped into the very deep end
because you weren't writing the libraries in TypeScript.
You were trying to define DTS files for your library,
which in my mind is like an order of magnitude harder.
Yeah, I decided that early on
that adding types to the library
was not really helping me.
It was very quickly that I just decided
it's going to be a rewrite.
And I just basically nuked all the code.
I mean, I kept the implementation code for a lot of it,
but basically, I just had to nuk everything
and start from scratch.
It was difficult because, like you said,
some of the dynamic parts of JavaScript
are just un type,
the way that we design APIs in old school JavaScript.
There's no meaningful way to keep and retain
and convey that type information.
So it requires you to rethink the way that you structure.
And I think some people see that as a downside.
They're like, oh, it's changing the flexibility
and the power that I have.
When in reality, it's probably being...
It's just making you think in a safer way
and it's making you structure your code a little bit better.
Like in no way do I think it degrades
any of the final output.
If anything, it's 10 times better.
It's just different.
Yeah, totally.
It's interesting.
Well, first off, it's really fascinating
to hear about your sort of compressed journey with this
because from going to little experience of types
to having these pretty robust libraries
being fully typed or whatever,
is this whole massive effort.
And you went through that really, really quickly.
Even the sort of cognitive shift from like,
oh, I don't really want to do this to like, okay, yeah,
this is the way is sort of an interesting thing
to hear about sort of what I still consider
a compressed timeline because I've heard stories
that are like many years in transition
of like really trying to come to acceptance with it.
But I think it's so true
that there are things that you pick up on
when you adopt TypeScript or like any type language.
But one of the things is,
I think about being more explicit with your intent,
you know, just like using it as a tool for communication
because you know, code is written for people
as much as it is for computers.
I think it's something that we don't value enough.
And then just going back to an earlier point too,
I had also, when I started trying to do TypeScript stuff,
I'd started trying to just like add like DTS files,
definition files to JavaScript projects.
And it was so, so, so hard.
One of the things is I found that that was not
the best documented workflow.
So TypeScript is generally, they,
they, I mean, they have good documentation,
but like that, the outer edges of type definition
can be a lot more complex.
So yeah, I don't know.

Interesting.
But that, that totally reflects my experience.
I remember you triggered a memory going back,
one of the very first individuals to help me out
with TypeScript was Brandon Byer,
creator of Blitz, like control.
And, you know, I remember being on a call with him
and he's just kind of showing me the ropes, you know.
And, and he was like, yeah, you know,
let's, let's design this API that you have in your mind for,
for this table library that you have.
And we started talking.
And he was just writing types and declaring functions.
But he didn't write any JavaScript at any point in time.
And I was like, how are you doing this?
This is so strange, you know.
And then, you know, he'd go to like call the function
and it was all just auto-completed and type saved.
And I'm like, but there's no implementation there.
And he was kind of the very first person
to help me really understand that,
that TypeScript meta type is, it's own language.
And really, she want to be productive.
And you want to feel powerful with TypeScript.
Because it's, it's really try writing a program
without doing any implementation code, you know.
Just declare types and functions
and just see how far you can get.
And I think that's really when you start to dive
into what TypeScript really is, right?
In fact, I need to, I need to pull up my Twitter
because there's a projects that I've been seeing lately
that got me really excited.
And let me, let me just scroll to my timeline here
for a second and see if I'm going to find it.
But let's see.
It is a meta TypeScript library
that is being developed.
Somebody that I know on Twitter, I can find it.
So, his handle is
Eccurb.
Eccurby, I'm not sure.
It's E-C-Y-R-B-E-D-E-V-S, his handle.
He's Tech Lead and creator of Zodios.
Zodios.org.
But he has been working on this new library,
call.
Basically, he's created like a programming,
like, it's almost like load-ash.
It's very much like TS Toolbelt, but for types.
And you can just, you can pipe types
through transformers and map things.
My goodness.
If we have to, I will find it and put it
the show notes after.
Oh, hot script.
There we go.
So, it's a library of composable functions
for the type level.
Transform your TypeScript types in any way
you want, using functions you already know.
So, the first example here is where he's
piping the integer 7 through a bunch of type transformers.
He's got tuples range, tuples take, tuples map,
match, tuples join.
And you can just see if you go down to all of the different
methods that they have implemented.
We're talking things like we've got tuple manipulation,
which is basically arrays, object manipulation.
There's unions and strings and numbers and booleans.
So, he created a JSON parser.
In TypeScript, like the parser JSON.
In the type level TypeScript, which,
and he created it in just a few lines of code.
It was really crazy.
I think that's something that people may not appreciate
with TypeScript, especially if you're coming
from a JavaScript world, TypeScript is your first
type language.
TypeScript actually has an incredibly,
incredibly powerful type system.
And, you know, it really goes to show that like Anders
knows his business.
Like, it's incredibly impressive.
Here's the link to that tweet where he shows a JSON parsing
in TypeScript type system in just 50 lines.
So, using HotScript, he creates a JSON parser
that will parse a string of whatever into JSON type.
Anyways, we don't need to go down that tangent any further.
But, you know, that can be like the capstone
of the TypeScript conversation.
But it really is incredible what you can do with it.
Yeah, it is crazy what you can do in the Type system.
The craziest thing I've done is I created a grid component
for our design system that takes a grid template
as a string and then breaks the template up into types
and returns grid cells that can only use those
as the area for their type.
So, it uses joins and trims.
And it's very hard to understand code
while you're looking at it.
But the types are like, oh, it's just doing
kind of like JavaScript-y things.
That's awesome.
Right.
Right.
Yeah, it's a lot of fun.
There's another thing that I wanted to talk about
before we move on and start talking about
particular libraries.
So, one of the things that you've done recently
is you've started making your libraries
not react specific.
So, making them where they're shareable
across different front-end frameworks.
I thought that was a really interesting choice
as well because like the migration,
the learning path from un typed to type to libraries,
going from a targeting and a single framework
to targeting at generic usage
is a really, really challenging thing to do well.
So, could you talk a little bit about
what motivated that and maybe even share
some of your experience about what you've learned
from that process?
Yeah.
That transition that's still happening
felt really natural
because of underlying technology,
innovations that were kind of happening.
So, I think the first one for me was hooks.
When hooks came out, it kind of kicked my mind
into reusable logic mode.
And no longer was I thinking like,
everything needs to be a component,
we can just have hooks.
And that kind of started detaching my brain
from the two and decoupling.
And I'm like, oh, you know what?
Headless UI is where it's at.
And the first library I used was downshift
from Kentsy Dodds.
I was like, oh yeah, headless is super cool.
So, I was like, let's headless out everything.
Let's go out headless for everything.
Right?
But still react.
And then at that point, I was like, oh,
this is interesting, it's reusable logic.
Like how much would it take to then decouple
that logic from react utilities that I was using?
Like react table, even when it was just a hook,
used to be implemented with a bunch of react hooks
under the hood.
It's like use, state, use, memo.
Everything's use memo and use callback everywhere.
And I was just like, this is,
but I wonder what it would take to move all that logic
into an agnostic layer.
Still hook it up the same way in a react adapter.
So, that's kind of what I did.
And I think it was just kind of this move to headless,
that it was like, oh, how headless can we go?
I'll be honest, I don't have any applications.
Maybe, no, I don't anymore.
I don't have any applications right now
that aren't react, like that I have in production.
So, I don't necessarily have an immediate use case
to be like, oh, let's go framework agnostic.
And let's, you know, I need that.
But it was easy enough, after I went headless,
it was kind of just a skip and a hop
to rip out all the react stuff and replace it with,
just kind of a, you know, a, you know,
kind of an adapter.
And that, that exposed some interesting patterns along the way.
You know, it reveals to me more and more clarity,
the pros and cons of react, you know,
the great things about it and the things that I'm like,
I don't really like that so much.
And then it started getting me thinking
about other libraries and frameworks.
And a lot of the challenges that came up,
that are still coming up,
in trying to make agnostic tools,
and the different ways that every single framework
thinks about reactivity.
And I know that signals are a really hot topic right now.
But they're very,
I think they're very important to talk about,
because most of the entire ecosystem outside of react
thinks in terms of signals or observables.
And they have for a while,
if you go back before react,
is it's kind of all about signals and observability
and kind of this mutative API for reactive system.
And react was kind of the very,
one of the first ones that was like,
you know what,
we're going to throw reactivity out the window.
And basically just going to create this massive immutable system.
And immutability honestly wasn't all that popular,
even in data, even in front end,
in data flow,
immutability wasn't super popular
until the react ecosystem really pushed it into overdrive,
including me.
Like I grew up on react with this immutable paradigm.
And so it was really interesting talking
in the early days with Ryan Carniata.
You know, he's showing me solid
and he's showing me signals
and all these different ways to approach reactivity.
And cause I wanted to build solid adapters
for all my libraries.
And it just really,
it really showed me that react is honestly kind of weird
in a great way.
Like it's really easy to,
it's easy to reason about data and immutability
when you're in that mode.
But it comes with a lot of weird immutable overhead
like use memo and use callback.
We're talking about compilers now.
So most of my learnings through going agnostic
have been shedding more and more light
on just kind of these weird differences
between these libraries.
And I think at the surface level,
everybody looks at view and react and svelte
and they see, they used to see maybe differences
and it was like template versus JSX, you know.
How do we do our markup and single file components?
And a lot of that is becoming, you know,
it doesn't matter anymore.
Like JSX is clearly superior, let's be honest.
But what really is coming down to matter for me
is just like the data flow and the data model.
And kind of the last big like aha moment
I've had with a lot of this is,
you know, I was looking at my spreadsheets
for like my budget and spreadsheets for things that work,
you know, and I'm looking at Excel
and I'm just like, what a fantastic performance system?
You know, Excel is amazing.
Basically built on signals, observability, right?
And you can create crazy UIs in Excel.
And it just got me thinking, you know,
maybe it's time to go back to signals.
Now all the talk about signals and everything.
So that's kind of been where my head had been
with moving agnostic with all this stuff.
It was very natural to kind of to push the boundaries.
And I've gotten to the point now where I'm pushing a little,
maybe a little too hard on some of the agnostic stuff.
And I've actually had to rebound a little bit.
And that's mostly in the router that I'm building.
If we want to talk about specific use cases.
All of the other tools I have, query, table,
virtualizer, a lot of these tools,
they fit really well into the agnostic,
like the agnostic ecosystem.
But the router is not one of those things.
Like early on, early on, I was like,
even in my hype video that's on my Twitter right now,
that I showed at Jamstack Conf, it was like agnostic router.
And it's like, could we build the agnostic router?
It's like, yes, I did it.
And you can build adapters for it.
But you wouldn't leave the hoops
that we were jumping through to get
a core router state management system
to work with both React and Solid.
It was, working with Ryan Carniata on this,
it was like either way, you're going to pay.
If you build your core state management
in an immutable fashion, you have to pay in performance
to get it to work in immutable system, like Solid.
And then I was like, what if we just wrote
the state management for all of our libraries
in signals and Solid and stuff like that,
like just signals.
And then we down sampled it to immutability for React.
And you pay big time going that direction too.
They're just so much at odds with each other.
And then, you know, over the last few weeks,
I've been on vacation for two weeks, to be honest.
And I got back, and one of the big realizations I had
is that every single framework has its own router
that's relatively first class for that framework,
except for React, for whatever reason.
I mean, we know the reason React was like,
we're just a UI library.
We're very un-pennin-y, you know.
At least that's how it was.
We could talk about the differences today, but,
but like, you know, React was one of the first ecosystems
où c'est, you know,
c'est possible de construire un grand,
un grand nombre de routers.
Et il y a beaucoup d'entre eux.
Obviously, React routers sont les plus populaires,
mais c'est Wouter,
Next.js, qui a un autre router.
Donc, en pensant à ça,
router est la seule libraire qui a dialé le temps.
Et je suis comme, ok,
pas tout doit être agnostic.
Router est un framework,
donc j'ai dialé le temps pour que 10 secondes de router réacte.
Et honnêtement,
le but est que je peux prendre les idées et le corps
de ça,
et poursuivre les deux autres routers de corps,
comme le solide routier.
J'aimerais que Brian
ré-write le solide routier,
pour être complètement type-safe et avoir tout ce genre de choses.
Donc,
Sorry, c'était vraiment long.
Donc, en fonction de ce que vous êtes en train de travailler,
vous avez mentionné que vous êtes en train de travailler sur un nouveau router pour réacte.
Donc, pourquoi un autre router ?
Comment est-ce différent et ce que cela fait différemment ?
C'est une bonne question.
Donc, oui,
c'est un nouveau router.
Je n'ai honnêtement pas voulu le construire
au début.
À Nozzle,
nous avons
un peu de dashboards,
et ces dashboards sont montrés
beaucoup de données,
des widgets,
des données d'exploration,
des dashboards,
et
essentiellement,
chaque dashboard est comme
une fonction
qui a
beaucoup de widgets
sur la table.
Et chaque des widgets
a ses paramètres de sa propre state.
Tout cela a besoin d'être installé dans le URL,
comme chaque un peu d'eux.
Nous avons regardé deux manières
de prendre le state et de hasher le site,
et de mettre le sur le server
et de partager les liens avec
les idées.
Et c'est juste une expérience terrible
de la façon dont ça se fait.
C'est tellement mieux d'élever
ce qu'il y a déjà dans le URL.
Donc, au début,
je voulais juste
déterrir
cet expérience d'expérience
pour que tous les widgets
et tout le monde
soient en place
dans le URL.
Et essentiellement,
cela a signé
une structure
de la structure de Json
qui représente
le state de chaque dashboard
et qui a trouvé
des moyens de se surmonter
en reliant le URL
et de le mettre en place.
Et ce n'était pas difficile
de me prendre mes mains
en arrivant
au début.
Je l'ai utilisé
le V5 React,
les paramètres de search,
et de faire
un moyen de réveiller.
Et ensuite,
je me suis dit
que je vais essayer
le React Router V6,
le V6 Beta.
Donc, j'ai utilisé le Beta
en nozzle
pour un moment.
Je n'ai pas fait beta
pour longtemps.
Mais ensuite,
quelques problèmes
qui s'éloignent
avec ça.
Le premier qui était très
apparent,
même de V5,
c'est que
quand j'ai commencé
à élever
tout le type script,
beaucoup de mes libraries
sont élevés.
Et puis,
les choses se sont mises
mieux,
mais l'une des choses
qui était
en train de lager
et de se mettre en place
était la logic de la route.
Et à l'abord,
je me suis dit
que la route n'est pas
si rapide,
il ne faut pas être super
de type.
Ensuite,
je me suis dit
que je vais crée
tous mes paramètres de search
sont en train de sortir
de la route.
Et ça va
conduire
la majorité de mon state.
Et en fait,
plus et plus
que j'ai été
en train de
utiliser le URL
pour le state,
ce qui est vraiment
un pattern très grand.
Et nous devons en faire plus.
So much of my state
was moving from
you state
into the URL.
And honestly,
if you think about
typescript and types
and state,
like that's where all of your
state comes out of
usually,
it's like you state.
Moving into the URL
is basically
moving it into this new black
box
that I lost all my types
and essentially had to start
like typecasting everything.
And that just,
it's terrible.
Because it doesn't track
through the entire system.
And it's just hacky.
And it kind of opened up.
I started opening up
this black box of like
typesafe URLs
and typesafe routing.
And I started learning a lot.
I started learning that like,
you know,
the URL
as a serialization destination
acts a lot like
some like an external
asynchronous resource
as you would with any other
external state.
When you fetch it
and you get it back,
you kind of have to validate it
because it's just coming
from a JSON string.
So similar to how people
are like writing Zod schemas
for,
you know,
their crud,
their crud fetches
and fetch and
GraphQL schemes
and whatnot.
The URL has that same
exhibits,
those same features
where you made,
you know,
you're essentially allowing
users to input
whatever the heck they want
into this box
on the screen.
And you need to reliably
parse it,
validate it,
set defaults
and at the same time,
type it
so that you have
a consistent
and reliable interface
coming out of it.
That entire pipeline
was just,
it's not that it wasn't,
it wasn't built in,
but it was even difficult
to do in userland
with ReactRouter v6.
I basically had wrapped
every function,
every export,
everything that I could get
out of ReactRouter v6,
I had wrapped
and proxied
until the point
where I was just like,
I've basically rewritten
my own router
just through this proxy layer.
That's how React Location
was born,
which was my toy router.
It wasn't type safe,
but it was me
kind of just getting a grasp
on how to build a router.
And finally,
this new router
is where I've tried
to rewrite
the routing experience
from the ground up
with types involved.
And so that means that
everything from
route definitions
even the nested definitions
stuff
is all type safe.
And the resulting types
are the resulting routing
structure that you get.
It's fully typed.
And then all of the APIs
for consuming
that routing structure
need to be fully typed as well.
Lots of different challenges
in each of those
different layers.
And we can talk about those
if you want,
I think they're all interesting.
But yeah,
types were the biggest
driver for this.
Obviously,
it wasn't too difficult
and very obvious for me to
it was obvious for me to
layer in things like
data loaders
and
actions
and and layer in
you know, nested routing
is kind of just the new
standard.
So
it was easy for me to take
some of the best parts of
React Router
and Next
and and Wouter
and
and just kind of put it all
together and build the system
that I wanted.
And it's got support for Zod
schemas
et
it's a lot of fun
and it's a joy to use.

some of the features
right out of the gate
that I can tell you
like all of the
all of the paths
are typesafe.
So you know,
it's almost
it's basically impossible
to write
you know,
write a link
that isn't typesafe.
So if a page
doesn't exist
or route doesn't exist,
you'll get
you'll get red squiggles for it.
If it requires search
programs,
you have to supply them.
They have to be the right type.
If you
if you're writing links
that are relative,
like it knows
about relative link navigation
and where you are.
It knows
all the types know
about your nested nature
of your routes.
So if you're in a deep,
deep component,
you're not just getting routes
or type type information
for the route you're in,
but the parent
the entire routing structure
is actually reduced
as you go down
the usage APIs.
So
you're down here at the
level C,
you've got type information
for A
going into B
and going into C
and kind of getting
intelligently merged
together
so that you know
of all of the search
programs
and all of the loaders
and all of the things
that you have
in them.
And even in the definition files
or the definitions themselves,
you know,
you can define search
parameters
in a top level route
and then have nested routes
and at the
in the definition itself,
it will know
about the types
that you have created
in parent routes
while you're in your definition
which is
is easier said than done.
So
it's in its required
some some interesting API
design
and engine
to to get around
this kind of stuff.

at the end of the day,
it's what I needed.
And so I'm
I'm actually still
hooking it up into nozzle.
But what it means is that
all these widgets
and all these crazy things
that I have
on all these different dashboards
when you
when you hover over a link,
I know of a surety
that it's going to a place
that exists
with search parameters
that are exactly
what they need to be.
And that the
that the URL
that's produced
is going to be exactly
what the user wants to see
and they can share it
collaborate with it.
teammates and
I mean just day one
of popping in a nozzle
I found like
just tons of bugs.
You know,
just trying to pass
search params
that don't exist
and passing numbers
or they should be strings.
So, yeah.
Must must be a good feeling
finding those bugs.
Good and sad.
You know,
Tanner,
you are an idiot.
Why did you do that?
You know,
but I mean,
I don't blame myself too much.
I didn't have typescripts.
So it's a lot of fun.
No, it's fun.
So so all of that type safety
comes pretty easily
from like the user standpoint.
I saw on the docs
you kind of do this
like one cool trick
to make the types available.
I think what you do
is like you declare your router
and then right below it
you do like a type script
declare and then declare an import
and that kind of just
makes everything work.

Yeah, because without that
you would have to provide
an API layer
that consumes the router
everywhere that you use it.
Which is still possible
if you for some reason
can't do it the way that we want.
So instead of doing that, you're right.
So we we use something
it's kind of an interesting hack
in typescript,
not really a hack,
but it's just an interesting pattern.
It uses declaration merging.
But what we do is we export
an empty interface
called a register
and we export that interface.
Actually, we're doing it across
almost all of the libraries.
Now there's just a register interface.
And what you can do
is you can declare that interface
and then under specific keys
for different libraries
like so in router,
it's the router key.
So you declare the router key
and then you pass type of
your router.
And what that does is it registers
because interfaces are mutable.
It registers the type of your router
into the inner type space
of like the entire module.
And what that means is that
now when you go to consume
exports right out of the library
like use route
or link
or something like that,
we can actually just reference
a special type inside of the library
called registered router.
And registered router is a
it's a conditional type
that says, hey,
if you know if
registered dot router
extends a router,
use that.
And if it doesn't,
then just use kind of
the default router type.
And that way,
you can provide type safety
to the rest,
to all of your top level exports
without ever having
to necessarily import your router
everywhere you go.
It's pretty great.
Yeah, super cool pattern.
We're actually using in react query
now in the next version.
So you're going to be able to
you can actually set the default error type
so that if across your entire system
you know that you're only,
you know, you want axios errors
or something,
you can actually
register
the error type
and pass axios error.
And that will set the default
generic
across the entire library
to be that.
So there's a lot of cool use cases
for it,
for setting type defaults
without having to,
without having being forced
to use APIs
that rely on
on scripted composition,
you know, to
to get that kind of default.
That's awesome.
You got some stuff
that you're working on in the future.
You've had
this tan stack brewing
and it's brought together
all these like disparate technologies
and we've heard chat
about some other future projects
that you have.
So there's this thing called bling
and some sort of other stuff in this space.
Could you could you sort of talk
about what some of your future plans
are here and what you're currently working on?
Absolutely.
So my future plans
can be summed up
by collaboration.
So essentially,
I'm always just kind of looking for
just the fastest way
to get something done
and
moving up into the upper spaces
of tooling,
you know,
getting into routing.
Routing is a significant portion
of what we would consider frameworks.
And after that,
a lot of what constitutes a framework
really comes down to,
you know,
usually there's some level of like
opinionated routing structure
for file systems
and then there's opinions
around deployment.
And then there's also opinions
around
kind of like
co-location of code
and server code
and full stack kind of things, right?
And so
after exploring what that would look
like for something like tan stack router,
I came to the conclusion that
you know,
a lot of these
a lot of these concepts about frameworks
are not really that unique.
The routing experience
and the framework
that it's built on
are super unique,
but meta frameworks overall,
they share a lot of the same tools,
a lot of the same goals.
And naturally,
you know,
there's a lot of prior art in React.
You know, you've got Nix,
you've got Remix,
you've got Gatsby.
A long, long time ago,
I even built React Static,
which was a static site generator framework
that I had to let go
back in the day.
But
I didn't want to
I didn't necessarily want to
recreate anything here
in this space,
but I wanted the ability to
to have a full stack framework
that I would also be able to use
my router in
because if I move to next,
I lose my router.
And I know they're working on type safe routes.
Good for them.
It's still, you know,
not exactly where I'd like it to be.
Maybe they'll get there eventually.
But then I also didn't want to have to be forced
into most of the next ecosystem as well.
If I move over to Remix,
it's the same story.
I have to drop my router.
Everything seems to be based around the router, right?
And I have strict ties to my router now.
I demand the type safety
I want out of my router.
And I demand the APIs that I have
to build these scalable solutions
that are really search param heavy.
And nobody else really seems to be
taking that seriously.
And that's fine.
It's mostly, you know,
it's not a massive use case
for a lot of other people.
But like if you were to go to AWS
or Google Cloud console,
look at that URL.
Clearly, they have built their own URL
because of how much state
they're storing in the world.
All right, they built their own router.
So I wanted to get into this space,
but I wanted to be able to use my own router,
but I didn't want to build
the rest of anything else.
So I talked about this to Ryan Carniotto.
We talk a lot, if you didn't.
If you didn't gather that.
And he was like, oh, it's really interesting.
You know, in solid,
we just kind of have our router,
but then we have, you know,
we have these transforms
that happen at bundle time.
And so he started looking,
he started showing me, you know,
the server dollar sign approach
they have for a lot of stuff.
And he's like, it's kind of like quick,
but it's not exactly like quick
is very proprietary.
And, you know,
they're based on resumability.
He's like, this is mostly just about code extraction.
And I was like, this is really interesting.
How could I use this for my framework?
And he's like, oh, I don't know.
And that kind of started the conversation
between me and him and Nikhil Sarov,
who is heading up a lot of like the work
under blame.
Like he's a lot of the brains behind it,
to be honest.
Nikhil was awesome.
And he started abstracting out
a lot of the logic from solid.
That allows it to do
kind of this full stack story.
Using these code,
this code extraction.
And recently, Ryan was on with
the quick team talking about code extraction.
And it was really great.
We'll have to find the link to that
and pop it in here.
But that's kind of how blame was born.
I imagined kind of this world where,
you know,
it didn't matter what framework you were using.
All that mattered was,
you know, you met a couple of dependencies
like VIT or Babel
or maybe we'd even support SWC,
right?
But at the end of the day,
you could do these code transforms
and the bundler would kind of separate your stuff.
And what happened was,
after we solved that piece,
just kind of a proof of concept,
the only piece that was left over
was the deployment story.
Again, something that I don't want to build.
I look at all the work that went into Next
and their deployment story
and remix and all the adapters
they maintained.
And I just didn't want to have to deal with any of that.
Well,
another good friend from Astro
is like top of Fred a lot at Astro.
And it's like,
look at Astro,
it's exactly what I wanted, right?
And me and Ryan were looking at it like,
dude, Astro,
they don't even know what they're sitting on.
It's gold mine, right?
They've built all the adapters.
They've built all the infrastructure
and they literally are very agnostic
about the front end.
So we decided,
let's build a new frame,
let's build a new frame,
let's start,
let's upgrade solid start,
eventually, right?
To say,
we're going to build everything on top of Astro
and just get deployments
and get compiler
and get all that for free.
Astro's built on top of VIT.
So all we have to do is supply VIT
functionality for bling.
And then we can wrap all that up
into an Astro plugin
and then you can just use
whatever router you want
and there's your framework, right?
You've got a router,
you've got code extraction
and you've got deployment.
And at the end of the day,
those are a lot of,
those are most of the core pieces
you need to have
what people think of as a framework.
And so,
Tansang Start itself
isn't even really a library yet.
There's nothing,
I mean,
there is a little bit there.
It houses the plugin
for Astro
that you import.
And then there's a couple of components
for doing react things like,
you know,
serializing
all the hydration data
and un serializing it
and hooking it up to Tansac router.
But aside from that,
that most of the logic
just sits inside of router
and inside of bling
and then all the great stuff
that Astro has given us.

it's still a work in progress.
But that's kind of the vision
that I have for it.
And, you know,
we're also very keen on kind of
the ecosystem moving towards
like react server components.
And I personally don't think that
server components
should necessarily be branded
or owned by a specific framework.
They're more of just a concept overall.
You know,
and we see the use server
and the use client
and that's just one way to do it, right?
But server components,
they're bigger than that.
They, they're like,
Astro technically had server components
for a long time ago.
But I think that everybody's converging
on these patterns.
And instead of chasing after
you know,
these patterns that will only work
in reactant
probably only work in next
for a while.
I'm way more interested
in pursuing agnostic,
framework agnostic solutions
to the same thing.
You know,
I think that
I think that with a little bit of work
and some collaboration
we could achieve
you know,
server components for all
with code extraction.
You know,
that that kind of fits
the majority use case.
Maybe not the whole resumability.
You know,
really, really granular stuff like,
like quick fit,
fits a good use case for everybody.
So that's kind of,
that's where we want to take it.
And to be honest,
we probably be a little bit
further,
but I've been on vacation for two weeks
and I'm kind of heads down
at Nozzle right now on some,
on some sprints,
but it's,
it's coming.
The day is coming where I think
we're going to have some good progress.
In fact,
I have a call scheduled tonight
with Ryan and Nikhil
where we're going to discuss
a lot of fun things.
So that's awesome.
Yeah,
that sounds super interesting.
Once it gets a little further along
we might have to have Nikhil on
to dive deeper into those topics.
Oh yeah.
The code extraction is really fun.
I mean,
you get into Babel
and AST transformations
and it's a lot of fun.

Want to hear a full conversation
with Tanner?
Subscibe to us on Patreon,
Spotify,
or Apple podcasts.
Okay.
That wraps it up for tooltips this week.
Thanks for coming on Tanner.
This was a lot of fun.
The conversation we had
about how like we started with
your beginnings and typescript
and how like
that's fed into what you're working on now
with Tan Stack Router.
I thought was super interesting
and I'm super excited to see
where you guys go with it.
Me too.
Yeah, Tanner.
Such a pleasure.
Use your stuff every day.
You've done great work.
You continue to do great work.
Yeah.
Just wish you the best.
You know,
and I also need to shout out
before we close out.
TK Dodo
and Kevin Vandy
and Pisic.
I mean,
these guys,
they maintain,
they help me maintain
or just flat out maintain
a lot of it.
Of those libraries that
that I don't,
I don't necessarily have time
to just be everywhere.
They are honestly what's
keeping these libraries up
in a flow
and making them better.
And just kind of all the
sponsorships that we have
on Tan Stack
is what makes that possible.
So they deserve
as much or maybe more
credit for a bunch of stuff
than I do.
So got to make sure
that they that they get there,
that they get their dues.
Yeah, for sure.
Absolutely.
Yeah,
l'un sentiment.

Episode suivant:


Les infos glanées

Je suis une fonctionnalité encore en dévelopement

Signaler une erreur

devtools.fm:DeveloperTools,OpenSource,SoftwareDevelopment

A podcast about developer tools and the people who make them. Join us as we embark on a journey to explore modern developer tooling and interview the people who make it possible. We love talking to the creators front-end frameworks (React, Solid, Svelte, Vue, Angular, etc), JavaScript and TypeScript runtimes (Node, Deno, Bun), Languages (Unison, Elixor, Rust, Zig), web tech (WASM, Web Containers, WebGPU, WebGL), database providers (Turso, Planetscale, Supabase, EdgeDB), and platforms (SST, AWS, Vercel, Netlify, Fly.io).
Tags
Card title

Lien du podcast

[{'term': 'Technology', 'label': None, 'scheme': 'http://www.itunes.com/'}]

Go somewhere