Comments (28)
Please forgive me for not knowing where y’all might be collaborating on an RFC. In the meantime, I’ll share an agnostic take on the patterns I’ve observed from Next and similar tooling. I’ll limit this take to html files, but I expect you can lift this to astro files if it is at-all useful.
File-System Routing
When using file-system routing, each file in a specified pages directory represents a route.
Static routes are represented by file paths relative to the pages directory.
Additionally, static directory routes (routes that might end with /
) are represented by index files.
File | Route |
---|---|
./pages/index.html | / |
./pages/about.html | /about |
./pages/blog/index.html | /blog/ |
./pages/blog/first-post.html | /blog/first-post |
Dynamic routes
Dynamic routes are represented by file paths that include square brackets.
Within a segment of a file path, a term between square brackets ([param]
) captures shallow routes. Adding three dots before a term ([...params]
) captures deep routes (which may include shallow routes).
File | Pattern | Example Request |
---|---|---|
./pages/blog/[slug].html | /blog/* | /blog/doing-the-web |
./pages/post/[...all].html | /post/** | /post/1984/mac/i386 |
./pages/[username]/settings.html | /*/settings | /joebloggs/settings |
The term between the brackets is used to identify any dynamically matched segment or segments.
Filename | Example Request | Example Identifiers (JSON) |
---|---|---|
./pages/post/[...all].html | /post/1984/mac/i386 | { "all": [ "1984", "mac", "i386" ] } |
./pages/[username]/settings.html | /joebloggs/settings | { "username": "joebloggs" } |
Static routes take precedence over dynamic routes. For dynamic routes, shallow routes take precedence over deep routes.
Filename | Pattern | Example Request |
---|---|---|
./pages/blog/[slug].html | /blog/* | /blog/doing-the-web |
./pages/post/[...all].html | /post/** | /post/1984/mac/i386 |
./pages/[username]/settings.html | /*/settings | /joebloggs/settings |
Single brackets ([param]
) do not capture empty segments (like their own directory route). Double brackets ([[param]]
, [[...all]]
) also capture empty segments. Single brackets take precedence over double brackets.
Consider the following file tree:
./pages/
├── post/
│ ├── create.html
│ ├── [pid].html
│ └── [...slug].html
./pages/post/create.html
matches/post/create
../pages/post/[pid].html
matches/post/1
,/post/abc
, etc, but not/post/create
,/post/1/2
, etc../pages/post/[...slug].html
matches/post/1/2
,/post/a/b/c
, etc, but not/post/create
,/post/1
, etc.- Optional, "catch all routes" would be defined using double brackets and dots.
Note: When file-system routing is paired with static assets (in separate pages and public directories), static assets take precedence over file-system routing.
from astro.
@eyelidlessness Let's take a step back for a moment as I think discussing technical details misses a larger reason for why .astro files look the way they do. When we started working on Astro our goal was to provide a way to write mostly HTML and sprinkle in components where you need them. Initially we weren't going to have Astro components at all. We didn't have expressions at all. The prototype for Astro used a regular HTML parser.
The .astro file format was never the goal, not the thing we wanted to try and sell. It evolved where it made sense to. I'm still hesitant about having complex expressions inside of .astro files, personally. To me that's what components provide. I say this all to say, we didn't set out focused on making a competitive component format. We set out to let you write HTML and components in the framework of your choice.
Switching to JSX I think would harm that goal. You'd be dropped into a dynamic programming language where you have to learn a bunch of special rules. Those rules are familiar to people coming from Next.js, but they're not if you're coming from anywhere else. It would also, I think, be a turnoff for Vue or Svelte or other framework users not already bought into JSX. We don't want to be a clone of Next.js with a few differences.
from astro.
For bigger, more complex sites SSR capability is pretty much a must (hundreds of pages, cookie personalization, constant headless CMS content changes). Generate performant pages + cache them via CloudFlare = ❤️
Next.js' API seems pretty well thought out, though it's a SSR by default and static HTML export is a secondary flow.
from astro.
This issue was closed when dynamic routes were released, but this seems to be just a mechanism for generating multiple static .html
files.
"adapters" for Node, Deno, etc.
What happened to the idea of a server-side runtime? The Astro programming model feels like a big improvement over what came before it, I just wish it wasn't a static-build-only tool.
Is the team still open to the idea of a server runtime? Is this tracked in another issue?
from astro.
@eyelidlessness We need the .astro file format in order to statically analyze a template, to know what parts are always static and which are dynamic (just expressions and components). Adopting JSX means everything is dynamic and we lose a big part of what makes Astro work.
from astro.
Wild idea: what if Astro wasn't a full server framework? Not all of one, anyway. Just1 make it mountable in existing frameworks.
If Astro's runtime could be mounted, it wouldn't need to worry about higher level routing, middleware (like auth), static assets (including .html pages generated by Astro at build time), sessions, etc. Astro's theoretical server runtime could accept the standard request/response args and return HTML
Astro would exist somewhere between a full web framework and a view engine. It could "route" the request in that Astro would pick which .astro
file to load up and render. But it would do this after the framework in charge has already handled the rest.
Hypothetical: "ΛstroLive"
I set up my main server.js
with the framework du jour. The framework parses the request, auths the user, populates the session from the db, etc. Then it also mounts ΛstroLive: myApp.use('/app', Astro.live('./path-to/astro-views', options))
2
Now I can write most of the fun parts of my app in Astro files! They have access to the original request object with things like session already populated by my framework. And the Astro files always return HTML.
I have no idea how this works for things like partial hydration. But Astro could be responsible for setting up internal paths for those resources at the root I set in the framework (/app
in the example above).
A key advantage, aside from not having to build and maintain a full JS web framework: it would be great for serverless.
🤔 Maybe this is a separate idea. or a convenient half way step to the ultimate goal.3
Footnotes
-
I say "Just", but I realize it's still a huge effort. ↩
-
I made up
Λ.live()
-- but I bet there's a cool space-y name someone more clever than I can come up with. ↩ -
If this is not in line with the goals of Astro, I won't mind. just something I've been thinking about. ↩
-
GitHub Markdown footnotes are awesome, btw. ↩
from astro.
Cloudflare links
- https://github.com/cloudflare/workers-types has some type definitions for the standard headers, params, formdata etc
- https://developers.cloudflare.com/workers/runtime-apis/html-rewriter looks promising for rewriting astro to html on the fly
- https://blog.cloudflare.com/introducing-htmlrewriter/
- The worker size limitation maybe an impediment - how many astro projects would grow to > 1MB
re/pages/[slug]
etc in previous projects I used/:POST|GET|HEAD/
etc as prefix to restrict methods
This is not normally used by client side frameworks though
from astro.
Inspired further by your existing collections work and these comments, I am wondering, could dynamic routes be expressed more simplistically in the file name, and then more expressively in their front matter?
Commence Bikeshedding
File paths with dollar-prefixed segments define static routes.
File paths with double-dollar-prefixed ($$
) segments define server routes.
Would the
$$
qualification immediately limit the number of directories and/or files prepared to "static all the things"?
File | Request Params |
---|---|
./pages/photo/$id.astro | id (static) |
./pages/photo/$id/edit.astro | id (static) |
./pages/photo/$$query.astro | query (dynamic) |
./pages/$$user/dashboard.astro | user (dynamic) |
./pages/post/$pid/$comment.astro | pid (static), comment (static) |
Additional routing restrictions are optionally exported from front matter as a method. The method receives a request
object and returns a truthy value if the request should be further handled by the file.
// ./pages/photo/$$query.astro
---
export const onRequestFilter = (req) => (
req.method === 'POST'
&& req.params?.query.length > 1 // note: req.params !== req.url.params
)
---
Conclude Bikeshedding
If acceptable, the server would have two-step filtration; the first being the more simplistic, file-system-generated filter; and the second being the more expressive onRequestFilter
method exported from front matter.
from astro.
This is probably another case that would benefit from copying prior art. Many similar products have had success just adopting Next/Gatsby APIs wholesale. This is not only good from the perspective of benefitting from those teams’ R&D, it also enables people like me to feel more comfortable pitching less established frameworks to clients who might be hesitant.
In that spirit—and I know this is going beyond the scope of this particular issue, but I think it’s worth discussing holistically—I would like to propose:
-
Revise
.astro
, the template format, to just be JSX/TS with a different file extension, or to be a configurable static-only format deferring to a user’s renderer of choice. (This could also be handled by the convention discussed in #74, as eg.astro.jsx
etc). -
Adopt Next’s APIs and filesystem patterns for pages, or the most common APIs in the user’s preferred ecosystem.
This would be a huge win for folks who may want a near drop in replacement for their not-as-static-as-advertised SSG/SSR framework. Imagine being able to say “switch from Next to Astro and cut your page weight 75%. It only takes a day.”
from astro.
I got confused by this too! But I think this issue was originally designed to track what we now just call SSR. Reopening to keep gathering feedback
from astro.
@chriscalo see here https://docs.astro.build/de/guides/server-side-rendering/
from astro.
Can you explain the second bullet point a little more? I don't follow what you mean by "register as dynamic". Thanks!
Looked into getServerSideProps
and I think that's a way to load asynchronous data before a page component is run. We have this in the form of "top level await" already. So yeah, I think I'm misunderstanding that bullet point. Would love some clarity.
from astro.
To help standardize across hosts I think we should make the following changes:
- The current
request.*
properties related to the URL should becauserequest.url
and be a URL object. - Add
request.headers
which is a headers object (Deno supports this as a global, environments that don't there's a impl in the fetch library).
from astro.
@jonathantneal Thanks so much for writing that up. I think that's almost exactly what we want (imo)! One additional type of route that we're currently working on is Collections routes. It's described in this PR: #68
It allows for paginated behavior without needing full dynamic support (paginated routes work in static rendered builds whereas dynamic likely won't).
from astro.
Can you explain the second bullet point a little more? I don't follow what you mean by "register as dynamic". Thanks!
Looked into getServerSideProps and I think that's a way to load asynchronous data before a page component is run. We have this in the form of "top level await" already. So yeah, I think I'm misunderstanding that bullet point. Would love some clarity.
Astro needs to know at build time: is this a buildable static page, or a dynamic runtime-only page? Static pages would be built to HTML, and dynamic pages would be built to JS to run in a server context.
from astro.
What's the reason for compiling a static page to HTML? Perf? As we've discussed previously, if we change the compilation to strings we're talking about a "dynamic" page module for a static page as being something like this:
function __render() {
return "<html><head><title>My app</title></head><body><h1>My app</h1></body><html>"
}
You can't get much better than that in terms of perf. So if that's the reason I don't think it's worth the added complexity of having 2 different modes and forcing the user to decorate their pages (which they might do incorrectly).
from astro.
I'm not sure I understand. JSX is also statically analyzable. The mechanism used in .astro
to determine whether a given component should be dynamic (currently :load
etc suffixes, possibly as props pending #73) could be used in JSX as well, using estree or your AST tooling of choice. This would reduce some burden on Astro (no need to maintain yet another template syntax), as well as cognitive load on users (no need to know yet another template syntax or use two in the same project).
from astro.
“switch from Next to Astro and cut your page weight 75%. It only takes a day.”
In am in no way attempting to judge your React or NextJS experience, but both of these projects have continued to evolve in significant ways, multiple times; including what many consider "paradigm shifts" in how one’s code base for a site are authored; and in particular with NextJS how significant aspects of file system routing are authored.
NextJS is fantastic! Even then, one could spend more than a day migrating a reasonable site from some older version of NextJS to some newer version. "I was there, Gandalf."
But their evolution is a feature; not a bug. This is to say — if Astro finds a better way to do something, and they ship it, and it gains reasonable traction, then their better way will be become the defacto way others want to follow, until someone finds an even better way again.
from astro.
Maybe we're talking about different things, but what i mean is that in a JSX file you can do:
export let render;
if(Math.random() < 0.5) {
render = () => <div>Like this</div>
} else {
render = () => <span>Totally different</span>
}
JavaScript is just completely dynamic, we chose a static template format for the same reasons as Svelte.
from astro.
In am in no way attempting to judge your React or NextJS experience, but both of these projects have continued to evolve in significant ways, multiple times; including what many consider "paradigm shifts" in how one’s code base for a site are authored; and in particular with NextJS how significant aspects of file system routing are authored.
This is fair, and surely could be a burden to maintain. Although at least in the last few versions of Next it's seemed to have mostly additive changes to an otherwise relatively stable API. So much so that it's quite common to find getStaticProps
, getStaticPaths
, getServerSideProps
etc in comparable tools.
But their evolution is a feature; not a bug. This is to say — if Astro finds a better way to do something, and they ship it, and it gains reasonable traction, then their better way will be become the defacto way others want to follow, until someone finds an even better way again.
This is possible, for sure. And I wouldn't suggest a better way might not be found, here or anywhere. I'm just saying it's been beneficial to other projects to adopt some or all of Next's APIs.
Maybe we're talking about different things, but what i mean is that in a JSX file you can do:
export let render; if(Math.random() < 0.5) { render = () => <div>Like this</div> } else { render = () => <span>Totally different</span> }JavaScript is just completely dynamic, we chose a static template format for the same reasons as Svelte.
Sorry for asking instead of trying it out first, I'm not somewhere I'd be able to try it out at the moment: isn't that possible in .astro
as well? It certainly seems it could be. The frontmatter-style heading appears to accept arbitrary JS/TS, as well as arbitrary imports from JS/TS. And it appears to accept JSX-style conditionals in template expressions. I'm still unclear on how static analysis of .astro
avoids this problem, but open to the possibility I'm missing something.
from astro.
prior art also includes vitedge c.f. https://github.com/frandiox/vitessedge-template
for cloudflare it is using webpack with config imported from node_modules folder
from astro.
I would like to +1 here that, as with many SSR solutions, routing is again "static":
- How do I make a multilingual website with translated routes?
- can I bring my own routing logic? our projects use a dynamic routing mapping logic that we load server-side and client-side, where each route is mapped to a rendering template
from astro.
+1 from my side, I realised this actually works on dev build.
So I would indeed think it could work something like Next.js JIT rendering?
I think all we would need is the params variable not?
file: $tweets.astro
builds: tweets/:tweet (singular)?
on page: $tweet is the ID?
I would hope there we can use this dynamic ID as a JIT rendering to retrieve some API details.
from astro.
Hi all,
Very new to Astro and apologies if this is not the right place for this. But bear with me and please consider the following:
---
// $pages.astro
export async function createCollection() {
// Flattened page list
const items = [
{
name: 'My Title',
slug: 'my-title',
fullSlug: 'my-title',
},
{
name: 'My Child',
slug: 'my-child',
fullSlug: 'my-title/my-child',
},
{
name: 'My Child 2',
slug: 'my-child-2',
fullSlug: 'my-title/my-child-2',
},
];
return {
route: `/pages/:fullSlug`,
paths() {
return items.map((page, i) => ({params: {fullSlug: page.fullSlug}}));
},
async props({ params }) {
return {item: items.find((page) => page.fullSlug === params.fullSlug)};
},
};
}
const {item} = Astro.props;
---
http://localhost:3000/pages/my-title
works :)http://localhost:3000/pages/my-title/my-child
doesn't work :(
The error I get for 2 is:
Error: [createCollection] route pattern does not match request: "/pages/:fullSlug". (/pages/my-title/my-child)
Now, coming from a Vue background which uses path-to-regexp
(as do you?).. I tried changing the returned route
property in createCollection
to route: /pages/:fullSlug+
(see here: https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#one-or-more)
Now I get the following error:
TypeError: Expected "fullSlug" to match "[^\/#\?]+?", but got "my-title/my-child"
Now for the reason of my post
By extending the behaviour of the route
param to follow all the rules of path-to-regexp, does this not solve the problem of "dynamic routing" ? Everything comes down to mapping the collections and the props with the route.
There would need to be rules around which takes precedence, static files vs regex patterns, and I'm sure I'm not taking into account a lot of things – but no one has mentioned this here yet and I thought it was worth doing so.
from astro.
This is out! https://docs.astro.build/core-concepts/routing#dynamic-routes Closing
from astro.
This is out! https://docs.astro.build/core-concepts/routing#dynamic-routes Closing
This sorts me out! Thanks
from astro.
This would be a wonderful feature. Ideally allowing for things like Catchall Routes that result in an Error404 page and allowing for content to update dynamically as it gets added.
What's the reason for compiling a static page to HTML? Perf? As we've discussed previously, if we change the compilation to strings we're talking about a "dynamic" page module for a static page as being something like this:
function __render() { return "<html><head><title>My app</title></head><body><h1>My app</h1></body><html>" }You can't get much better than that in terms of perf. So if that's the reason I don't think it's worth the added complexity of having 2 different modes and forcing the user to decorate their pages (which they might do incorrectly).
I feel like this or something akin to it would be a great compromise. Having half static and half dynamic pages would require whatever SSR code is running to do additional checks when having to retrieve static content.
I think Astro and in particular, .astro
files lend themselves uniquely to SSR being logically split into the "build" and "render" steps. Allowing the "build" step to happen when the request fires with top-level await would make for some very elegant solutions to resolving path information and similar.
from astro.
Wild idea: what if Astro wasn't a full server framework? Not all of one, anyway. Just1 make it mountable in existing frameworks.
If Astro's runtime could be mounted, it wouldn't need to worry about higher level routing, middleware (like auth), static assets (including .html pages generated by Astro at build time), sessions, etc. Astro's theoretical server runtime could accept the standard request/response args and return HTML
Astro would exist somewhere between a full web framework and a view engine. It could "route" the request in that Astro would pick which
.astro
file to load up and render. But it would do this after the framework in charge has already handled the rest.Hypothetical: "ΛstroLive"
I set up my main
server.js
with the framework du jour. The framework parses the request, auths the user, populates the session from the db, etc. Then it also mounts ΛstroLive:myApp.use('/app', Astro.live('./path-to/astro-views', options))
2Now I can write most of the fun parts of my app in Astro files! They have access to the original request object with things like session already populated by my framework. And the Astro files always return HTML.
I have no idea how this works for things like partial hydration. But Astro could be responsible for setting up internal paths for those resources at the root I set in the framework (
/app
in the example above).A key advantage, aside from not having to build and maintain a full JS web framework: it would be great for serverless.
🤔 Maybe this is a separate idea. or a convenient half way step to the ultimate goal.3
So something with the similar energy of https://tokio.rs/blog/2021-07-announcing-axum where all the middleware is just express or ember or something?
Footnotes
-
I say "Just", but I realize it's still a huge effort. ↩
-
I made up
Λ.live()
-- but I bet there's a cool space-y name someone more clever than I can come up with. ↩ -
If this is not in line with the goals of Astro, I won't mind. just something I've been thinking about. ↩
-
GitHub Markdown footnotes are awesome, btw. ↩
from astro.
Related Issues (20)
- `noUnusedLocals` and `noUnusedParameters` Settings Not Applied in Astro Build HOT 3
- Redoc react component not rendering HOT 5
- Inline script for component is added to the page even if component is absent on the page HOT 2
- Dependency Dashboard
- Vitest fails because of the ImageFunction helper (with content collections)
- [Bug]: WordPress Rest Api does not optimize images - set:html HOT 2
- "Browser APIs are not available on the server" error in `client:visible` component HOT 1
- Popup works as expected in dev mode but not in the build
- "Transition was aborted because of invalid state" coming form hoisted.*.js HOT 4
- files[normalizedPath] is not a function
- `@astrojs/lit` - `Module '"astro"' has no exported member 'AstroIntegration'.` HOT 1
- @astrojs/rss uses trailing slash in urls when it shouldn't HOT 1
- Add a new `Astro.metadata` global object HOT 6
- SVG rendering error - "unsupported file type" HOT 2
- VIewTransitions break on presence of <input name="action"> HOT 2
- cannot dev or preview a page, if the page filename contain 'index', eg. e-index.astro HOT 2
- onTouchStart not being attached to DOM elements when using jsx HOT 8
- Rendering React component does not work HOT 4
- `@astrojs/mdx`: “smart quotes” are broken in HTML headers HOT 2
- React hydration error with react table but works fine the same example in next.js HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from astro.