Custom Routes #569
Replies: 4 comments 6 replies
-
Hi @blittle @morganmccunn, it's great to see a discussion around this important topic. For product page For product list Current shopify/liquid limitations are that route parts and product handle are not localised. The implementation that @blittle is proposing seems a good start. and the possibility to have a part of the route which is an id to a key-value dictionary ex //routes dictionary
dictionary = {
'shoes': {
'it': 'scarpe',
'en': 'shoes'
}
} This requires a companion component and hook (to be used from controllers) to create a navigation helpers that can read from this dictionary I suggest to create in hydro some hook that when the user change locale or country the path will be updated accordingly so //SetLocale.client.jsx
[locale, setLocale] = useLocale()
onLocaleChange(newLocale) {
setLocale(newLocale)
}
// useLocale.hook.jsx
setLocale(locale) {
_set:ocale(newLocale);
setServerState('locale', newLocale); // needed to trigger the react server component refresh /react
updateRouteVariable('locale', newLocale) // needed to update the route pushing a new element in the history
} if you don't want to do this in a too much opinionated way you could create two different hook NOTE:
The main goal of this topic to me is
|
Beta Was this translation helpful? Give feedback.
-
@blittle I think this looks great. The ability to centralize our routing logic at the entry-server level would be a big win. It would make it so we don't have to duplicate redirect logic across multiple Page Components when porting over our legacy Shopify Theme redirects. In your proposal, the list of custom routes is hardcoded into the codebase. Have you considered what this might look like if you had a list of redirects that was managed externally? I'm also curious how you're thinking about dynamic routing use cases (e.g. dynamically splitting traffic between 2 pages for an A/B test). Do you feel like that would be a custom routing concern, or maybe more of a Server Component concern? I'm wondering if we could benefit from another type of route where a (potentially async) function can return a Server Component. Something along the lines of: const customRoutes = [
{
route: '/products/:handle',
component: async ({ request, response, params }) => {
let abTestVariant = request.cookies.get('my-ab-test');
if (!abTestVariant) {
abTestVariant = await determineAbTestVariant({ params });
response.headers.set('Set-Cookie', `my-ab-test=${abTestVariant}`);
}
return {
component: abTestVariant === 'page-a' ? PageA : PageB,
props: { myCustomProp: abTestVariant }
}
},
}
]; |
Beta Was this translation helpful? Give feedback.
-
@nschomberg @bitforcesrl the proposal has been updated. |
Beta Was this translation helpful? Give feedback.
-
Curious - is this an inherent constraint of RSC, a design decision, or just a current limitation? I can see why it's this way in theory at least, but curious the actual technical reasoning. One nice thing about client-side is that you can animate into your new state immediately, show a loading skeleton, and then fill in once data comes in. This feels nicer to me than the alternative of "leave old state around with a loading indicator". Is it true that the latter isn't possible with RSC or is it just a Hydrogen decision to not ship client-side routing / fetching? |
Beta Was this translation helpful? Give feedback.
-
January 28, 2022 - Original Proposal
February 9, 2022 - Proposal 2
February 10, 2022 - API Routes Clarification
February 14, 2022 - Remove Asynchronous Route definition example
RFC Hydrogen Custom Routes
Currently routing in hydrogen is provided by the file system. If you want to create a new page, simply add a new
.server.jsx
component to thepages
directory of your project. While this is simple and easy to get started with, it becomes difficult to manage at scale with thousands of routes and redirects. The file system also make advanced features like localized paths (/products
->/productos
) very difficult.Custom Routes is a proposed new way for developers to define Hydrogen routes independent of the file system.
Original Proposal
Our original proposal for custom routes involved defining a manifest, that would be passed directly to
renderHydrogen
:While this is pattern is familiar to many developers, and inspired by NextJS, we've reviewed it internally and externally, there are a number of shortcomings with the approach:
New Proposal
We believe that all of the above problems can be solved by embracing React itself to define custom routes. Instead of providing a static manifest to
renderHydrogen
, we'll introduce a Hydrogen specific<Route>
component that can be used withinApp.server.jsx
:This should look very similar to Hydrogen before the 0.10 release. We removed react router in 0.10 because server components do not yet support context (which React Router relies on). We've worked with Meta to come up with a short term solution that brings a limited form of context back. With this new approach, we can bring back the React-Router-like approach. We feel this is superior to the manifest approach for a number of reasons:
1. Dynamic by default and declarative React
This approach is dynamic by default, in that the routes are defined during the request life-cycle. You can easily customize what routes are rendered based on the current request:
You can also completely remove the default file system routing by removing
<FileRoutes>
:Thousands of routes defined directly in
App.server.jsx
can become difficult to manage. Instead, use standard React component composition to split into multiple server components:2. Extensibility
Future routing extensibility can be based in React. The community can build custom Routing components without waiting for us to extend the functionality of a manifest approach. For example, someone in the community could build Redwood Route Sets without waiting for Hydrogen.
Additionally, we can still build a manifest approach that just maps to underlying
<Route>
components.Relation to API Routes
API Routes exist outside React. Because of this, custom routes defined by React
<Route>
components cannot be an API route. API routes must exist within the file system. Custom Routes are only for defining React pages.Custom Router API
Routes Component
A new
<Routes>
component will be introduced. It will not initially have any props. If you would like to remove all default routing provided by Hydrogen, just remove<Routes>
. All routing must be defined within children. Once<Router>
is rendered, the request will immediately be processed.Props:
fallback
- What to render when no route is foundExample:
FileRoutes Component
This is the
DefaultRoutes
component renamed. This is where Hydrogen's default file based routing lives. Remove fromApp.server.jsx
if you do not want to use any file system routing. Must be a child within<Router>
.Props:
routes
- A file glob object fromimport.meta.globEager
Example:
Route Component
A new
<Route>
component where you can define a custom route. Must be a child within<Router>
.Props:
path
- Required to match a path. May contain parameters:/products/:handle
.page
- The component to render. Can be a component reference.Examples
Redirect Component
A new
<Redirect>
component to declaratively define redirects more easilyProps:
path
- The path to match. May contain parameters:/products/:handle
to
- The destination path to redirect to. May contain matched parameters.permanent
- Iftrue
will use the 308 status code which instructs clients/search engines to cache the redirect forever, iffalse
will use the 307 status code which is temporary and is not cached.Questions
Are there performance issues with dynamically defining routes on each request?
Yes. Doing so is an advanced use case. We don't expect most hydrogen sites to need to do this. It would be bad for every request to Hydrogen to hit a third party service to get route definitions. We expect Hydrogen in the future to work well with redirects defined in Shopify's storefront admin. We will introduce a
<ShopifyRedirect>
component with sensible defaults in how often external routes are fetched.What happens when there are route collisions?
It is possible to define duplicate custom routes, and custom routes that also match file system routes. The first one defined will always win. For example:
Why not use React Router?
Beta Was this translation helpful? Give feedback.
All reactions