Since next 9 we have finally dynamic routes: https://github.com/zeit/next.js/issues/7607 馃憤
The one important thing missing, but mentioned in the RFC is the ability to define catch-all pages and i did not find an open issue that tracks the progress of that.
E.g. if you have a list of pages (from a cms or so) that can be nested, where every page has a path (that might contain slashes /), you can't currently match them with dynamic routes. A possible workaround would be (if you limit the depth of the paths) to do nested multiple pages.
i think the solution proposed in https://github.com/zeit/next.js/issues/7607 would be good ( pages/website-builder/[customerName]/%.tsx))
One thing to consider is:
can the page define whether it accepts a path or not? E.g. consider this pattern: content/%.tsx
. Given this url-path: "content/about/team", i would receive this segment: about/team
and then e.g. fetch a page somewhere from a cms with this path. If this page does not exist, i would like to show a 404, so the page should "reject" this path.
A first-class code-driven routing approach that uses path-to-regexp without custom server would be much more flexible in the future. I think forcing the route pattern to be in the filenames has too much downsides (filesystem restrictions) without adding much value to developers, but that's my opinion.
I'm building out a prototype of my company's site with NextJs. With the updates in NextJs 9, I can build all of our pages EXCEPT one, that uses a "pretty" url setup for its search results page: /search/keyword/somekeyword/category/somecategory
. For the whole site, I would need to build a server.js
, JUST so I can handle this one page.
If this was implemented, I could set up a component in /pages/search/%.js
and reasonably handle everything after /search
in the getInitialProps
.
This is a fantastic idea and in my opinion would convince a lot of people to ditch whatever server they're using and use NextJs and their filesystem instead.
by the way, I can't find a workaround at all for this use case. Even with a custom server i am lost.
i want to send all non-static page to one catch-all page. This is only possible if the custom server knows either about all my dynamic pages or about all my static next pages.
imaging you have these paths somewhere in a database. This list will be loaded through graphql or similar.
/about
/about/team
/service
/service/you-name-it
and you have these static next pages:
index.jsx
profile.tsx
user/[userId].tsx
Now, with a custom server, i would need to declare a route like this, e.g. in express:
app.get('*', function(req, res) {
// here you use const handle = app.getRequestHandler(); normally
// I would already have to know which routes exist in my db to either call
// handle(req, res)
// or to send it to my custom catch-all-route
// so i would already have to know which request would be accepted by nextjs without error, or which paths exist in my db.
});
ok i found a (somewhat silly) workaround, assuming the depth of your dynamic pages paths is limited:
you can uses as many subfolders as you like, depending on the depth of your dynamic pages
import * as React from "react";
import { withRouter } from "next/router";
const ContentPage = withRouter(({ router }) => {
// assuming usePage loads a page from a database/graphql with the given path
const { page } = usePage(router.asPath);
return <Whatever page={page} />
});
export default ContentPage;
and in any other index.js
in this tree:
import page from "./[slug]";
export default page;
<Link />
to that page, you need a little trick, e.g. a component like this:import Link from "next/link";
import React from "react";
// assuming page is an object with .path , e.g. path=/about/team/bigboss
const PageLink = ({ page, ...props }) => {
const depth = page.path.split("/").length - 1;
return <Link href={"/" + new Array(depth).fill("[slug]").join("/")} as={page.path} {...props} />;
};
export default PageLink;
This works, however it has a huge problem: When going to a childpage, an addition, identical bundle of that page is also loaded
Best workaround is to use https://github.com/fridays/next-routes
Would probably a good idea to integrate that into core. That would solve so much problems.
EDIT: after further testing, this does also not work properly: https://github.com/fridays/next-routes/issues/315
basically, you can't have file system routes anymore, you have to define all routes in next-routes
Edit2: I now got rid of next-routes again and do it like this:
server.get("*", async (req, res, n) => {
if (req.url) {
const hasPage = await hasPageForPath(req.url);
if (hasPage) {
return app.render(req, res, "/content", { path: req.url });
} else {
n();
}
}
});
where hasPageForPath
checks whether the url exists.
In my case hasPageForPath
looks like this:
const hasPageForPath = async (path) => {
const { page } = await request(
GRAPHQL_ENDPOINT,
/* GraphQL */ `
query GetPage($path: String!) {
page(path: $path) {
id
}
}
`,
{ path },
);
return Boolean(page);
};
this might look expensive, but can be cached.
@timneutkens is it possible to get a "order by priority" list (not ETA) for some next features?
I guess this one looks "important" for most ppl using Dynamic routes, and 9.2 sounds like "far away" from current priorities.
Right now working around it using rewrite in our reverse proxy, but forces us to add "route logic" to an external service.
You can see here: https://github.com/zeit/next.js/milestones?direction=asc&sort=due_date it's not far away.
I think a catch-all would be great. Lets say you would have a headless CMS of any depth of routes are built inside. It would be great to tell NextJS that [index].tsx would handle everything, either "/about" or "depp/nested/page". Currently I use next-routes but it would be great if the logic could be avoided.
That was my exact use case, using it with a headless cms, but had to discard NextJS for now.
It's been a few months, and looks it will take a few more until it's available.
That was my exact use case, using it with a headless cms, but had to discard NextJS for now.
It's been a few months, and looks it will take a few more until it's available.
I would not discard next.js because of that, because there are workarounds.
But you need a custom router for that:
// e.g. if someone accesses /depp/nested/page, we check whether a page at that path exists
// we exclude some static paths like fonts and static
server.get(/^\/(?!(_|fonts|static).*)/, async (req, res, n) => {
if (req.url) {
try {
// hasPageForPath checks whether a page exists (in our case it makes a small graphql request to the cms-api
const hasPage = await hasPageForPath(req.url);
if (hasPage) {
// there exists a page at that path. We render our content.tsx page with ?path=/deep/nested/page
return app.render(req, res, "/content", { path: req.url });
} else {
n();
}
} catch (e) {
// tslint:disable-next-line:no-console
console.error(e);
n();
}
}
});
// if the previous route did not catch the route, this one will catch it and pass it to nextjs to perform the default behaviour
server.all("*", (req, res) => {
handle(req, res);
});
Handled by https://github.com/zeit/next.js/issues/9081
Most helpful comment
ok i found a (somewhat silly) workaround, assuming the depth of your dynamic pages paths is limited:
you can uses as many subfolders as you like, depending on the depth of your dynamic pages
and in any other
index.js
in this tree:<Link />
to that page, you need a little trick, e.g. a component like this:This works, however it has a huge problem: When going to a childpage, an addition, identical bundle of that page is also loaded