Problem/Motivation

Users will need to be able to link to specific components on a node and, later, specific settings views/panels/modals. To facilitate this we should use a routing library. For example we will want to enable links such as /xb/component/3 to open the contextual sidebar for a specific component or /xb/settings/page to open a modal in which a user can set things like the page title and url.

There are 3 popular contending popular router libraries that we can pick from

https://tanstack.com/router/latest
A powerful React router for client-side and full-stack react applications. Fully type-safe APIs, first-class search-params for managing state in the URL and seamless integration with the existing React ecosystem.

https://github.com/molefrog/wouter
Wouter is a tiny router for modern React and Preact apps that relies on Hooks.

https://reactrouter.com/en/main
React Router is a lightweight, fully-featured routing library for the React JavaScript library. React Router runs everywhere that React runs; on the web, on the server (using node.js), and on React Native.

My feelings, briefly, are

Tanstack router is popular and makes a big deal of being typesafe. I see some criticism about it's complexity and it seems heavily tied to SSR with Next.js which is not something that this project requires. It may be slightly overkill for our use case however it will certainly not limit us.

Wouter is small and lean. It seems to offer the functionality this project needs and has a low overhead and small but functional API. It is the least prolific though .

React Router seems to have quite a lot of negative sentiment around the latest v6 release compounded by a history of making breaking changes. It has been combined with/build in tandem with Remix which is not something we are using in this project. It has a large community and is very well documented and powerful.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

jessebaker created an issue. See original summary.

lauriii’s picture

@jessebaker Which of these options would you recommend?

jessebaker’s picture

I'm spending my day today trying to come to a conclusion on that. I have not used any of them before (although I did briefly investigate react-router during our initial estimation phase) so I'm doing some investigation/exploration. I opened the ticket in the hope that others might put forward their opinions before we have to take a decision.

balintbrews’s picture

Great summary and assessment, @jessebaker! Here are my thoughts in addition, hoping they could be helpful to make the final decision.

I think of the three libraries as three different options on a spectrum that goes from small and lean to robust and “enterprise” — wouter and TanStack Router being at those two endpoints, respectively.

Based on my understanding, the primary role of a routing library in XB will be the ability of deep linking. (Besides the code organization benefits that come from using a router instead of manually managing application state/context.)

Based on that, to me the easiest option to rule out first would be TanStack Router. It can do quite the heavy lifting, with its included devtools, its focus on type safety, all those things that are great when you’re working on a fully-featured app with complex data fetching logic.

Comparing wouter and React Router, I don’t think XB could go wrong with either, because the routing code in XB will be minimal, and since wouter already mimics some parts of React Router, it would be easy to switch between the two if that becomes necessary for any reason.

If I had to choose, my vote would go for React Router for the following reasons:

1. It’s a critical part of Remix, which is owned by Shopify. Hydrogen, Shopify’s headless stack is built on Remix. They invest heavily in the ecosystem (e.g. they recently announced Hydrogen Visual Editor — btw, there's probably some useful inspiration for XB there).
2. Many React devs already know React Router (or a version of it), as it existed long before Remix. (The team who built React Router built Remix.) For a long time, there wasn’t really a question of what router solution you choose if you have a React project, so many React devs have used a version of React Router in the past. I’ve heard from agencies that have older single-page applications they built for clients with React, that Remix is really great for them, because they can easily migrate, since the router solution they used is the same as what's in Remix. (Now, I realize this may not be as straightforward as it sounds due to the breaking changes between major versions.)

roaguicr’s picture

Seconding the @balintbrews comment, React Router is heavily used in modern React apps and is well-known by most React developers (who may eventually contribute?). Probably the best approach.

wim leers’s picture

To expand on #3: @jessebaker built a PoC during the risk minimization early days of XB that used React Router: https://git.drupalcode.org/project/experience_builder/-/tree/poc_reactro...

Based on #3 + #4 + #5, it seems pretty clear that React Router is the strongest contender (especially given the swappability mentioned in #4).

@jessebaker, let's go? 😄😄

jessebaker’s picture

Thanks to everyone for the input so far. I think as @Wim Leers says, it seems we have a pretty strong consensus forming around React Router and my quick look at all 3 last week has confirmed that I don't think Tanstack's offering is the right fit for us. Given the apparent ease of switching between the other options I'm going to write a basic React Router integration and we can go from there.

wim leers’s picture

Assigned: Unassigned » jessebaker
Status: Active » Needs work

Aight, signaling that new issue status 😄

ctrladel’s picture

I don't have any specific feedback on which library is best, react router has generally done everything I've needed.

We'll want to make sure there's a good path for integrating Drupal's translations with whatever option is chosen. I've had challenges in the past making sure small SPA type components are able to effectively use Drupal's translations. From what I've found interface translations are heavily dependent on Drupal's html responses either through the initial page response or ajax responses rendering twig or using Drupal.t() in javascript which is dependent on drupalSettings being populated with the initial page response or an ajax response. This poses a challenge when you want to get the translated title for a pop up or component edit pane that is only rendered from JS without requiring an html request to Drupal.

larowlan’s picture

Can you elaborate on why we need a router? At present all of the important parts are covered in the redux slices, eg selected component, open panes etc

We could easily add a listener middleware that used history.pushState and UrlSearchParams to represent these as url query Params. Then all we need to do is read these back when creating the store

I've got plenty of client projects I could adapt for a POC of this

jessebaker’s picture

@larowlan I'm thinking that I want to enable a user to be able to copy/paste the URL to direct a colleague to a specific part of the experience builder.

It's very early days yet so at first /xb/component/:componentId will open the Contextual Panel for a specific component.

Later we may want to go as far as having /xb/page/:pageId/component/:componentId/settings/styles to link to the style settings tab of a specific component on a specific page.

jessebaker’s picture

@larowlan I'm going to be mostly AFK for the next day or so and haven't been able to make as much progress on the React Router implementation as I would have liked.

If you have a PoC you can share without spending a huge amount of time, I'd be interested in seeing your proposed solution.

larowlan’s picture

wim leers’s picture

Thanks, @larowlan! 😄

wim leers’s picture

What's next here?

bnjmnm made their first commit to this issue’s fork.

jessebaker’s picture

Current Status:

Due to my ongoing challenges in getting Cypress tests working locally, there is still a requirement for tests on this work.

Tests that still need to be written are:

  • [e2e] A test that visits the XB, selects a components and checks that the URL is /xb/component/
  • [e2e] A test that visits the XB, selects one component and then another and then uses the back button in the browser and is able to navigate back through the two selected components and back to /xb.
  • [e2e] A test that visits the XB and opens the component sidebar (to the correct component) via the
    element in the TopBar.tsx

The first test ensures that setting the selectedComponent state updates the URL.
The 2nd ensures that the browser history states are working correctly.
The 3rd ensures that navigating via the URL updates the state.

wim leers’s picture

Assigned: jessebaker » Unassigned
Issue tags: +Needs tests

Reflecting #19 in the issue metadata — and the fact that @jessebaker is going on PTO tonight.

That means this issue is ready to be pushed to completion by writing the required tests documented in #19 👍

bnjmnm’s picture

  • The contextual form seems to be loading an older version of the form - I'm guessing the MR moved some stuff around which would up preserving some code that has since been refactored
  • Clicking a component to make it active/selected takes us to /xb/component/ which is great, but refreshing a page on that URL results in a 404. While I'm very OK with incremental progress, refreshing the UI page is something I do frequently while developing for it and I'd like it to at least redirect back to /ui
bnjmnm’s picture

Re #21

  • The contextual form seems to be loading an older version of the form - I'm guessing the MR moved some stuff around which would up preserving some code that has since been refactored Turns out it was old stuff that was still there but we didn't see due to it being black text/black BG.
  • Clicking a component to make it active/selected takes us to /xb/component/ which is great, but refreshing a page on that URL results in a 404. While I'm very OK with incremental progress, refreshing the UI page is something I do frequently while developing for it and I'd like it to at least redirect back to /ui I pushed a fix for this.
bnjmnm’s picture

Status: Needs work » Needs review

hooroomoo made their first commit to this issue’s fork.

  • hooroomoo committed 1c224d27 on 0.x authored by jessebaker
    Issue #3459235 by bnjmnm, jessebaker, hooroomoo, balintbrews, roaguicr:...
hooroomoo’s picture

Status: Needs review » Fixed
Issue tags: -Needs tests
wim leers’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.