sveltejs / sveltejs/kit

Reusign nested subroutes

Open
#11,855 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
20.8k
Forks
2.3k
Avg merge
1d 16h
Merged PRs (30d)
156

Description

Describe the problem

Currently, there is no easy way to reuse a set of subroutes across other routes.

Simple example

For example, let's say that you have a 3-step modal for creating something.
You may want to access the modal from

/landing/modal
/landing/modal/step1
/landing/modal/step2

/settings/user/modal/
/settings/user/modal/step1
/settings/user/modal/step2

There is no clean way to reuse the /modal/* routes.

Real case

I have a website about sports, and I have the following routes:

/dailies  --> 2 column layout
/live/feed --> 2 rows layout
/strategy/[stratId] --> 2 columns layout
/filters --> 1 row + 1 row with 2 columns layout

In all those cases, the left (top) column (row) will show a predefined list of matches in different configurations and appearances.
Clicking the match will display the match data in the left (bottom) row (column). The match data displayed is the same no matter where you access it. The navigation for the matches looks like this:

/match/{matchId}
/match/{matchId}/tab1
/match/{matchId}/tab1/subtab1-1
/match/{matchId}/tab1/subtab1-2
/match/{matchId}/tab2/subtab2-1
/match/{matchId}/tab2/subtab2-1
/match/{matchId}/tab3

What I want is to have all of the following routes:

/dailies
/dailies/unique-daily-stuff-cant-be-used-with-matches
/dailies/match/{matchId}
/dailies/match/{matchId}/tab1
/dailies/match/{matchId}/tab1/subtab1-1
/dailies/match/{matchId}/tab1/subtab1-2
/dailies/match/{matchId}/tab2/subtab2-1
/dailies/match/{matchId}/tab2/subtab2-1
/dailies/match/{matchId}/tab3

/live/feed
/live/feed/unique-feed-stuff-cant-be-used-with-matches
/live/feed/match/{matchId}
/live/feed/match/{matchId}/tab1
/live/feed/match/{matchId}/tab1/subtab1-1
/live/feed/match/{matchId}/tab1/subtab1-2
/live/feed/{matchId}/tab2/subtab2-1
/live/feed/{matchId}/tab2/subtab2-1
/live/feed/{matchId}/tab3

(...)

/filters/match/{matchId}/tab3

As you can see, the matches subroutes are repeated across all higher-level routes, and the page I want to render is the same; their position will change.

Describe the proposed solution
  • Add a new syntax to the routing system &(group-name)
  • Let's name the token a Shared Group
  • A folder named &(group-name) at the routes root indicates a group of routes that can be shared
  • A shared folder is not generated by itself nor does it create routes by itself
  • Adding a file named &group-name.ts in a folder will automatically "insert" all the routes from the shared group into that folder
  • any potential configuration would be added to the above mentioned .ts file
Example:
routes/
├── dailies/
│   ├── something/
│   │   └── +page.svelte
│   ├── otherthing/
│   │   └── +page.svelte
│   └── match/
│       └── &matchGroup.ts
├── live/
│   ├── feed/
│   │   ├── match/
│   │   │   └── &matchGroup.ts
│   │   ├── subfeed/
│   │   │   └── +page.ts
│   │   └── +layout.svelte
│   └── not-feed/
│       └── +page.svelte
└── &(matchGroup)/
    ├── [matchId]/
    │   └── +page.svelte
    └── +layout.svelte

Would generate something similar to:

routes/
├── dailies/
│   ├── something/
│   │   └── +page.svelte
│   ├── otherthing/
│   │   └── +page.svelte
│   └── match/
│       ├── [matchId]/
│       │   └── +page.svelte
│       └── +layout.svelte
└── live/
    ├── feed/
    │   ├── match/
    │   │   ├── [matchId]/
    │   │   │   └── +page.svelte
    │   │   └── +layout.svelte
    │   ├── subfeed/
    │   │   └── +page.ts
    │   └── +layout.svelte
    └── not-feed/
        └── +page.svelte
Alternatives considered
Option 1 - do nothing and let the user replicate the routes.
  • Let the users manually create the nested replicated folder structure
  • Keeps the parity between folder structure and routes
  • Users can use components to have a single code shared between all routes as exemplified on the docs
  • Creating new subroutes that need to be shared will, however, require users to create the new routing folder in all higher level router.
    • On the real example above: Adding a tab4 requires creating 4 new folders and 4 new individual +page.ts files that will be exactly the same. Forgetting to create one of these folders will result in a broken navigation.
Option 2 - Do nothing and encourage users to use route parameters and svelte:components
  • Structures like /[category]/match/(...) would allow for a single route that reuses the subroute
  • Combined with param matching and an svelte:components the [category] both +page and +layout can be dynamically selected to change between upper route options
  • This falls apart when categories have other subroutes that differ between them
  • To add a new route, you need to update at least three files, the matcher, the +page, and the +layout from the upper route (if they exist)
  • It is annoying to have that many switch statements for alternating the [category]
  • The folder structure gets less intuitive
Option 3 - Move away from file-based routing
  • Moves away from one of the project's core decisions
  • Time consuming
  • Breaking change
  • One of the breakiest changes that could happen ?
Option 4 - Different syntax or structure for reusing routes
  • Please do iterate on the proposed syntax, I have not dedicated the amount of effort needed for something this complex
  • This current syntax tries to reuse concepts that already exist on sveltekit
    • The current syntax borrows from the group syntax (group) and expands it to a "shared" group
    • It also borrows from the breaking out of layouts syntax layout@.svelte
Importance

would make my life easier

Additional Information

Angular (that does not use file-based routing) had a pretty good solution: create a new module with the subroutes and import it as the child of any other existing route. It looks something like:

[
  {
    path: "dailies",
    component: DailiesComponent,
    children: [
      { path: "unique-daily-stuff", component: UnrelatedComponent },
      {
        path: "match",
        loadChildren: () => import("./match.module").then((m) => m.MatchModule),
      },
    ],
  },
  {
    path: "live",
    component: LiveComponent,
    children: [
      {
        path: "feed",
        children: [
          {
            path: "match",
            loadChildren: () =>
              import("./match.module").then((m) => m.MatchModule),
          },
          {
            path: "unique-daily-stuff",
            component: UnrelatedComponent,
          },
        ],
      },
    ],
  },
];

Despite not being file-base it exemplifies what would be the goal of this proposal.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No implementation files or tests are named. Start by reviewing SvelteKit's existing group syntax, layout syntax, and routing generation behavior referenced in the proposal. Done would mean shared route groups can be inserted under multiple parent routes without generating standalone routes, while preserving each parent's layout and unique routes.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, typescript
Domain
frontend, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.