I've done something similar in a React project I'm working on to avoid dealing with the insanity that is react-router.
Call me naïve, but routing in a single page application is just not that hard of a problem. At the core it's about having a piece of state¹ (your active route) which determines which part of the app you want to render–something you can do with a switch statement². On top of that, you want to synchronize that state to the page URL³.
Doing it yourself requires more boilerplate code, no question about it. But it's not that much code tbh (not very complex either), and you get back control over that important piece of state, which otherwise remains opaque and difficult to work with–i.e., its "shape" is pre-determined by the routing library you use. For example, react-router doesn't support parallel routes.
I also basically re-invent a tiny 50 line router for every web project. Hardly ever go beyond "Map URL -> Page" and "Map URL + Parameters -> Page with properties", and when you do, knowing 100% how it works helps a lot.
I also agree it isn't a hard problem, but personally I'd say you got the flow the wrong way around. You don't want to "synchronize state to the page URL" but rather treat the page URL as something you create state from, so it works both when you navigate there by pressing anchor tags, or the user manually enters the URL, and it gets a bit easier to manage.
Basically, URL is the top-level state, and from there you derive what page, then render that page, rather than the other way around.
Yeah, implementing it with data flowing one-way only from the URL to the state is cleaner.
Conceptually, however, I prefer to think of my state being at the center of things. I mean, that's where I define (via types) what the state is. The URL is just one serialization of that state that is convenient to use in a web browser (making it work with links, back/forth buttons, etc). Maybe in another environment another serialization would be needed. Or maybe no serialization could be needed at all (making it a memory router).
I don't think it's naive, I often do the same in Vue. A pretty useful subset of vue-router can be implemented in less than a tenth of the bundle size.
This seems really irrelevant to the browser. I wonder why this was standardized, JavaScript is easily powerful enough to express this. Surely it hasn't been a performance bottleneck for it needing a native api?
I agree with the other two comments, surely almost every frontend webdev has implemented a router in their career unless they never strayed from the major frameworks. It's really not a complicated thing to have to build. I'm not one to look a gift horse in the mouth but I don't see why we're being given this one.
Anytime I end up wondering "Why do we have feature X on the web?" I tend to up end reading through proposals and I always end up finding a suitable answer that makes me wonder no more. For this specific feature, there is lots of prior discussions about why this is needed in the first place here: https://github.com/whatwg/urlpattern
Based on https://github.com/whatwg/urlpattern/blob/main/explainer.md it looks like they specifically wanted it as a way to scope service workers so it's easy to make them only run on certain parts of a site, and viewed giving people something easy to use for other URL matching as a nice bonus.
Kind of obvious when a DOM/Web API is mentioned, isn't it? Besides, does it really matter? Same ideas you use for building a frontend router can be used to build a backend router, and vice-versa.
Maybe not so much for an Internet Protocol router?
[deleted]
Is this really that much easier than matching paths and query strings yourself? I'm glad that there is an official API now, but this article didn't really show me anything to get excited about as someone who has built several client-side routers from scratch.
Writing a fast router can be surprisingly difficult if you are trying to cover the edges. The state of the art is not trivial:
Whose implementation, specifically? I don't think as specified URLPattern has any inherent performance drawbacks compared to the alternatives, but it seems like V8/NodeJS/Deno definitely didn't thought closely and/or clearly about performance when they did theirs.
All three of those use the same implementation. I would hope that this will be improved over time, but it's not a guarantee by any stretch of the imagination.
The thing is, performance in that particular context hardly matters, unless you're forcing users to switch pages faster than 1 page per second. Even if each resolving takes 0.1 seconds (which be bad, don't get me wrong), 99% of users wouldn't notice a thing, and if you're a small agency/shop/company/team, focusing on more general things tends to be time spent better.
I have a hard time imagining how one would implement a router that took a full millisecond to resolve a path in a typical case.
Imagine doing a call to a remote authentication service to check who has access to what :) Regardless, performance is usually not the biggest problem in those types of routers.
I'm really glad we have URLPattern, as it's a fairly mundane task that shouldn't need us to bring in special tools. I can't imagining argue that the plarform shouldn't come well equipped for basic cases like this. Ideally a native implementation will be able to use SIMD and be faster too, which is a nice to have.
Unfortunately cases like here in the article are an example of why URLPattern had enormous push back during development. It's not the worst thing in the world to run three or four patterns one after another. But generally, doing a linear search for the right pattern doesn't scale well at all! If there's a dozen routes, running pattern after pattern is probably not ideal!
We can look at Hono routers, for example. Which has a bunch of different examples, most of which will one-shot a bunch of patterns at once. RegExpRouter, TrieRouter. There's good write-ups for the tradeoffs across them. But they all take a bunch of routes up front, and combine them into a single matcher, attempting to re-use work across patterns. https://hono.dev/docs/concepts/routers
Really good we have URLPattern, but this is the anti-use-case, that almost convinced folks not to make URLPattern at all.
I've done something similar in a React project I'm working on to avoid dealing with the insanity that is react-router.
Call me naïve, but routing in a single page application is just not that hard of a problem. At the core it's about having a piece of state¹ (your active route) which determines which part of the app you want to render–something you can do with a switch statement². On top of that, you want to synchronize that state to the page URL³.
Doing it yourself requires more boilerplate code, no question about it. But it's not that much code tbh (not very complex either), and you get back control over that important piece of state, which otherwise remains opaque and difficult to work with–i.e., its "shape" is pre-determined by the routing library you use. For example, react-router doesn't support parallel routes.
¹ https://github.com/superegodev/superego/blob/main/packages/a...
² https://github.com/superegodev/superego/blob/main/packages/a...
³ https://github.com/superegodev/superego/blob/main/packages/a...
I also basically re-invent a tiny 50 line router for every web project. Hardly ever go beyond "Map URL -> Page" and "Map URL + Parameters -> Page with properties", and when you do, knowing 100% how it works helps a lot.
I also agree it isn't a hard problem, but personally I'd say you got the flow the wrong way around. You don't want to "synchronize state to the page URL" but rather treat the page URL as something you create state from, so it works both when you navigate there by pressing anchor tags, or the user manually enters the URL, and it gets a bit easier to manage.
Basically, URL is the top-level state, and from there you derive what page, then render that page, rather than the other way around.
Yeah, implementing it with data flowing one-way only from the URL to the state is cleaner.
Conceptually, however, I prefer to think of my state being at the center of things. I mean, that's where I define (via types) what the state is. The URL is just one serialization of that state that is convenient to use in a web browser (making it work with links, back/forth buttons, etc). Maybe in another environment another serialization would be needed. Or maybe no serialization could be needed at all (making it a memory router).
I don't think it's naive, I often do the same in Vue. A pretty useful subset of vue-router can be implemented in less than a tenth of the bundle size.
A very tiny router is provided by nanostores: https://github.com/nanostores/router
This seems really irrelevant to the browser. I wonder why this was standardized, JavaScript is easily powerful enough to express this. Surely it hasn't been a performance bottleneck for it needing a native api?
I agree with the other two comments, surely almost every frontend webdev has implemented a router in their career unless they never strayed from the major frameworks. It's really not a complicated thing to have to build. I'm not one to look a gift horse in the mouth but I don't see why we're being given this one.
Anytime I end up wondering "Why do we have feature X on the web?" I tend to up end reading through proposals and I always end up finding a suitable answer that makes me wonder no more. For this specific feature, there is lots of prior discussions about why this is needed in the first place here: https://github.com/whatwg/urlpattern
Based on https://github.com/whatwg/urlpattern/blob/main/explainer.md it looks like they specifically wanted it as a way to scope service workers so it's easy to make them only run on certain parts of a site, and viewed giving people something easy to use for other URL matching as a nice bonus.
Page.js is a good alternative for a client-side router https://visionmedia.github.io/page.js/
SPA router, not IP router.
Kind of obvious when a DOM/Web API is mentioned, isn't it? Besides, does it really matter? Same ideas you use for building a frontend router can be used to build a backend router, and vice-versa.
Maybe not so much for an Internet Protocol router?
Is this really that much easier than matching paths and query strings yourself? I'm glad that there is an official API now, but this article didn't really show me anything to get excited about as someone who has built several client-side routers from scratch.
Writing a fast router can be surprisingly difficult if you are trying to cover the edges. The state of the art is not trivial:
https://github.com/dotnet/aspnetcore/blob/main/src/Http/Rout...
You're assuming that URLPattern is performant... That doesn't seem to be the case currently.
https://adventures.nodeland.dev/archive/you-should-not-use-u...
https://github.com/denoland/deno/issues/19861
> assuming that URLPattern is performant
Whose implementation, specifically? I don't think as specified URLPattern has any inherent performance drawbacks compared to the alternatives, but it seems like V8/NodeJS/Deno definitely didn't thought closely and/or clearly about performance when they did theirs.
All three of those use the same implementation. I would hope that this will be improved over time, but it's not a guarantee by any stretch of the imagination.
The thing is, performance in that particular context hardly matters, unless you're forcing users to switch pages faster than 1 page per second. Even if each resolving takes 0.1 seconds (which be bad, don't get me wrong), 99% of users wouldn't notice a thing, and if you're a small agency/shop/company/team, focusing on more general things tends to be time spent better.
I have a hard time imagining how one would implement a router that took a full millisecond to resolve a path in a typical case.
Imagine doing a call to a remote authentication service to check who has access to what :) Regardless, performance is usually not the biggest problem in those types of routers.
I'm really glad we have URLPattern, as it's a fairly mundane task that shouldn't need us to bring in special tools. I can't imagining argue that the plarform shouldn't come well equipped for basic cases like this. Ideally a native implementation will be able to use SIMD and be faster too, which is a nice to have.
Unfortunately cases like here in the article are an example of why URLPattern had enormous push back during development. It's not the worst thing in the world to run three or four patterns one after another. But generally, doing a linear search for the right pattern doesn't scale well at all! If there's a dozen routes, running pattern after pattern is probably not ideal!
We can look at Hono routers, for example. Which has a bunch of different examples, most of which will one-shot a bunch of patterns at once. RegExpRouter, TrieRouter. There's good write-ups for the tradeoffs across them. But they all take a bunch of routes up front, and combine them into a single matcher, attempting to re-use work across patterns. https://hono.dev/docs/concepts/routers
Really good we have URLPattern, but this is the anti-use-case, that almost convinced folks not to make URLPattern at all.