vike
vike copied to clipboard
Single Route File
Description
Being able to define the entire routing of the app in a single file.
// vike.config.h.ts
import type { Config } from 'vike/types'
import LandingPage from './pages/LandingPage.js'
import ProductListPage from './pages/ProductListPage.js'
import ProductPage from './pages/ProductPage.js'
export default {
pages: [
'/': {
Page: LandingPage
},
'/product/@id': {
Page: ProductPage
},
'/products': {
Page: ProductListPage
},
// ...
]
} satisfies Config
If you want this, feel free to write a comment down below explaining why. The more we know why users want this, the better we can prioritize accordingly.
Edit: Redacted outdated design.
One option could be to use a tagged template literal string. Which at first glance seems to have typescript support. It would also allow to eliminate the documentation/comments in the aforementioned example, since it would be more direct.
function Menu() {
return <>
<Link to={`/`} />
<Link to={`/about`} />
<Link to={`/product/${42}`} />
<Link to={`/products`} />
</>
}
I see there’s some precedence to the API that I suggested: https://solito.dev/usage/link#wrapper-components
In all, Solito does some really nifty things with routing, which is very desirable to replicate on a non-NextJS stack. To enable universal apps cross platform.
The downside with Fernando’ stack (NextJS+Expo+Solito+Dripsy) is that it actually disables SSR on web to be able to work (due to issues with responsivity, that Tamagui solves).
So there’s a double opportunity there: universal apps on Vite and with SSR on web. I know the very competent and prolific author of Tamagui is looking into that, and has been eyeing vite-plugin-ssr since I gave word to him about it.
Open question: how to make it work with TypeScript? So that TypeScript complains upon
<Link to="unknown-page" />
or<Link to="product-list" id={"42"} />
(because"42"
is astring
but should be anumber
).
Does Solito has such TypeScript support?
One option could be to use a tagged template literal string.
So far, I can't see how TypeScript's template literal strings would help.
One aspect of Single Route Files that is interesting is that the user can change the URL without having to modify each link. That's why I'm leaning towards <Link to="product" id={42} />
. But I can see that to be a matter of taste and use case. Small apps don't need that.
Does Solito has such TypeScript support?
It doesn't seem like it. It simply replicates NextJS' API.
So far, I can't see how TypeScript's template literal strings would help.
On second glance, I think you're right. TypeScript Template Literal Types that I linked to are a different thing. What I had in mind - type checking inside tagged template literal strings - is still an open TypeScript issue (not very likely to get implemented, it seems): https://github.com/microsoft/TypeScript/issues/29432
change the URL without having to modify each link
I see. I think the general web native way of doing it - and what Rails did - is to simply provide a re-route to the new URL. That way, outdated links in ones app would work the same as the outdated links to one's page that had already been shared on the web: they would be re-routed to the new URL.
Open question: how to make it work with TypeScript? So that TypeScript complains upon <Link to="unknown-page" /> or <Link to="product-list" id={"42"} /> (because "42" is a string but should be a number).
Did some more research into this, since you mentioned it is a blocker.
In addition to pathpida, check out next-type-safe-routes or nextjs-routes or next-typed-routes for inspiration.
The API of next-type-safe-routes seems the nicest (clearest, most straightforward/intuitive, least boilerplate). The automatic route listing (route autocomplete) is quite nice.
next-type-safe-routes parses the /pages folder in your Next.js app and generates types for all the pages and API routes in the application. These types can then be used to ensure that you only link to pages (and only use API routes) that actually exists in your application.
due to the strictness, we can also determine which parameters are needed for dynamic routes.
So, the only issue with using either of those directly is that they all presume filesystem routing (being for NextJS).
Maybe typesafe-routes is what you're looking for?
Actually, it turns out you can use TypeScript Template Literal Types to get type checking inside tagged template literal strings, like I had in mind. This library does it, see the GIF animation in that repo.
As long as it is possible to wrap the <Link>
component that vite-plugin-ssr
provides, so that a Solito.dev like library could be developed on top, for universal/crossplatform-app support. With the goal of having vite-plugin-ssr (Vite) instead of NextJS (Webpack) as the base for such crossplatform apps.
Neat. Indeed, infer
with template literal types is a solution.
We can even make the route types defined by the user in route.ts
available globally by using declare module
(e.g. https://telefunc.com/typescript#getcontext).
I think we can make it work.
Although Route Functions routes cannot be typed, but that's expected.
As long as it is possible to wrap the component that vite-plugin-ssr provides
Yes, that's the idea. So far, I don't see a problem with that.
Came across this. Could be potential inspiration for typesafe routes.
solito’s useParam() hook takes in a parse() function to let you turn the string → number. i’ve found it to be nice for dealing with numbers and validating enums it comes with type safety + inference too: https://solito.dev/usage/params#parsing-values from Fernando's tweet
react-router-url-params is another one, for typesafe query parameters. It was made this summer.
My idea is that TypeScript for query params just works if we treat param serialization as implementation detail defined by the user.
// `order` is typesafe
<Link to="product-search" order={"desc"} />
// /routes.js
export default {
routes: [
{
Page: '/pages/product-search.page.js',
toUrl: ({ order }: { order: 'asc' | 'desc' }) => `/product/search?order={order}`,
route: '/product/search'
},
]
}
I'm a big fan of Tanstack React Location and upcoming Tanstack Router. I think it's API is quite well thought. See this single route file example from Tanstack Router: https://github.com/TanStack/router/blob/572318b1b132307be02857b97ae05b2040363ccf/examples/kitchen-sink/src/index.tsx#L91
I really like how React components can be used instead of file path as proposed in this issue. Also, pending state feature sounds incredible.
The API of React Location / TanStack Router looks a lot like the API of (Remix and) React Router v6 : https://gist.github.com/redbar0n/f6ec12264ff9d58e243cc516e4e3f41b?permalink_comment_id=4312122#gistcomment-4312122
TanStack Router has the same issue as React Router mentioned there:
the component tree custody is duplicated: once in React and once in React Router... Because React Router directly drives React.
The overall design is starting to crystalize (it also includes better support for nested layouts).
One open question: https://stackoverflow.com/questions/73957822/get-import-path-of-dynamic-import-as-typescript-string-type, in case someone knows this. I don't think it's possible, but who knows with TypeScript 😅.
blitz-js has another way to achieve typesafe routing which looks quite neat:
https://twitter.com/aleksandrasays/status/1559157742559825920?s=20&t=uaV2ruG_UcOlWxDwGhk9yQ
blitz-js has another way to achieve typesafe routing which looks quite neat:
https://twitter.com/aleksandrasays/status/1559157742559825920?s=20&t=uaV2ruG_UcOlWxDwGhk9yQ
👍 Neat idea to make the link target a TS object, so you can definition-jump directly to the route file. Neat.
@patryk-smc @brillout here's an idea for a simplified API which presents the routes in a flat manner so you can more easily visually pattern-match to the URL in the browser (compared to the nesting in React Router and TanStack Router):
https://gist.github.com/redbar0n/f6ec12264ff9d58e243cc516e4e3f41b?permalink_comment_id=4343793#gistcomment-4343793
@redbar0n Yes I'm also not too fond of their verbosity. While I share the sentiment to keep things succinct, I'm thinking multi-dimensional arrays are a bit too extreme, i.e. not explicit enough. Let's see if the solution I come up with is succinct enough (so far I think so).
Ok, excited to see how it will look. I wonder how much explicitness is needed if we can have some Convention over Configuration.
Coming from React Router I use their Javascript Route Definitions which allows me to dynamically create and pre-filter routes, and render navigation from the definitions.
For folks who need/want Single Route File, please PM on discord.
FFR, here is a thread with people who don't like file based routing: https://twitter.com/t3dotgg/status/1583285667768827904
I think SolidStart is taking a great approach with the dynamic <FileRoutes />
component https://start.solidjs.com/core-concepts/routing
I think SolidStart is taking a great approach with the dynamic
<FileRoutes />
component https://start.solidjs.com/core-concepts/routing
Yeah, I agree, with one exception. To quote the SolidStart approach:
Under the hood, SolidStart traverses your routes directory, collects all the routes, and makes them accessible using the <FileRoutes /> component. The <FileRoutes /> component only includes your UI routes, and not your API routes. You can use it instead of manually entering all your Routes inside the <Routes /> component in root.tsx. Let the compiler do the boring work!
I think it makes more sense to expose the file routes through some other means than as a UI component. Maybe as a function (e.g. import getFileSystemRoutes()
like the getRoute
that next-type-safe-routes does).
You then use it to import the default routes derived from the file system, which you are then able to overwrite in the routes file. Or you could choose to not import it and rather manually write out all the routes if you'd like. I think it could be the best of both worlds: convenience of filesystem routing (aka. file based routes) + the power and customizability of a single route file.
Avoiding having it as a UI component would also allow vps to remain render library agnostic.
I'm actually thinking of increasingly focusing on single route files, and I'm increasingly thinking that it's superior than Next/Solid/Remix's approach. That said, so far, I think still supporting basic FS routing makes sense (like what VPS already does today), but I'll likely won't focus much on it.
TanStack Router vs. React Router DOM vs. Next.js comparison page is out, as per demand. It has a good overview of potentially desirable features for a routing solution.
TanStack Router vs. React Router DOM vs. Next.js comparison page is out, as per demand. It has a good overview of potentially desirable features for a routing solution.
Other than typesafe links (which is quite lovely), is there something you'd miss if using VPS's built-in router?
Not that I can think of (the search params API seems neat, but not sure if it's in scope).
The ability to easily swap out the router and use any router would be nice. Even if it required a one-time mapping from the vps router to that router (say, for SSR).
easily swap out the router
In principle, it should be possible to deeply integrate any router using the onBeforeRoute()
hook.
But, tbh and AFAICT, I see VPS's router to be superior for its intended purpose than what I'm seeing so far.
The only thing missing is typesafe links, which require either code gen or opting-out of Filesystem Routing (i.e. what this ticket is about). (AFAICT TypeScript types can't be deduced from the mere presence of a file, so it's either or.) I think it's possible to do code gen in a seamless manner without the user even noticing.
Ticket for typesafe links: https://github.com/brillout/vite-plugin-ssr/issues/698.
Now that the V1 design is out, this ticket should be fairly easy to implement.
To increase the priority of this ticket, add a comment here with the reasons why you want this.