Skip to content

Feature request: specify layout per route through module export #1110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
UltraCakeBakery opened this issue Apr 18, 2021 · 10 comments
Closed
Labels
feature / enhancement New feature or request router

Comments

@UltraCakeBakery
Copy link

Is your feature request related to a problem? Please describe.
My progressive web app requires the following routes:

- index.svelte
- sign-up.svelte
- reset-password.svelte
- reset-email.svelte
- dashboard.svelte
- settings.svelte
- logout.svelte

This generates the following URLs that I am sadly not allowed to change due to annoying customers not wanting to clear their search history:

- /index
- /sign-up
- /reset-password
- /reset-email
- /dashboard
- /settings
- /logout

The index, sign-up, reset-password and reset-email routes share the same layout (layoutOne).
The dashboard and settings routes share a completely different layout (layoutTwo)
And last but not least: the logout should have no layout set at all

Describe the solution you'd like
I want to override what layout a route should use by doing something like:

export const layout = 'layoutName' // Or directly pass the component.

This would do the same as having a $layout.reset.svelte file in the same folder, but only for that specific route.

Describe alternatives you've considered

  1. Wrapping my routes in layout components. This would however lead to unnecessary re-mounts.
  2. Using a single $layout component and try using the $page store and some if/else statements to change the layout based on the current path.

Option 1 works but makes page transitions and smooth animations almost impossible
Option 2 is difficult due to the amount of pages I have. Writing 20+ regexes and maintaining the massive if/else statements in one layout component is not something I'm looking forward to do.

How important is this feature to you?
This feature missing makes migrating to Svelte Kit impossible for my team and me.

Additional context
I swear I heard Rich Harris talk about this or someone from the svelte team showing some pseudo code that looks like what I am proposing here. I couldn't find anything in both the Sapper repository and in discord so if I missed something consider this issue never submitted.

@acoyfellow
Copy link

acoyfellow commented Apr 18, 2021

I'd solve this all in the $layout.svelte file. (your option 2)
Just have different layouts / wrappers for each

{#if layout==='one'}
<div class="one"><slot/></div>
{/if}

{#if layout==='two'}
<div class="two"><slot/></div>
{/if}

so, it's not a showstopper really, just you'd have an subjectively "ugly" $layout.svelte

@benmccann benmccann changed the title Feature request: $layout.secondary Feature request: specify layout as module export Apr 19, 2021
@benmccann
Copy link
Member

I've renamed the issue title because $layout.secondary was not mentioned anywhere in the description and so didn't seem relevant to the issue as described

Exporting an option from the route file would be quite difficult to implement for the reasons described in #1061 (comment) so I expect it wouldn't happen

@benmccann benmccann added the feature / enhancement New feature or request label Apr 19, 2021
@UltraCakeBakery
Copy link
Author

I've renamed the issue title because $layout.secondary was not mentioned anywhere in the description and so didn't seem relevant to the issue as described

Exporting an option from the route file would be quite difficult to implement for the reasons described in #1061 (comment) so I expect it wouldn't happen

The title was a placeholder. I honestly thought I changed it 🤦🏻‍♂️

A alternative could be that we can override it in the config? Something like:

module.exports = {
  router: {
     layoutOverrides: {
         '$layout.secondary': ['dashboard.svelte', 'other-route.svelte']
     }
  }
}

It is messy but in my opinion it still better than not being able to do this at all haha.

@UltraCakeBakery
Copy link
Author

UltraCakeBakery commented Apr 19, 2021

I'd solve this all in the $layout.svelte file. (your option 2)
Just have different layouts / wrappers for each

{#if layout==='one'}
<div class="one"><slot/></div>
{/if}

{#if layout==='two'}
<div class="two"><slot/></div>
{/if}

so, it's not a showstopper really, just you'd have an subjectively "ugly" $layout.svelte

If I wanted to take this route (pun intended) I need some way to compare against the route component. This is currently not doable through the $page store. This makes dealing with route aliases and/or dynamic routes a big pain in the bum.

@stephane-vanraes
Copy link
Contributor

An alternative route, partially based on @acoyfellow's approach is to leverage the ContextAPI for this:

<-- $layout.svelte -->
<script>
  import { setContext } from 'svelte';
  import { writable } from 'svelte/store';
  const layout = writable('layout1');
  setContext('setLayout', layout.set);
</script>

{#if $layout === 'layout1}....{/if}
<!-- dashboard.svelte -->
<script>
  import { getContext } from 'svelte';
  getContext('setLayout')('layout2');
</script>

The disadvantage of this is of course that you need to add some extra code to each page to set the layout, but you would have to do so anyway.

@UltraCakeBakery
Copy link
Author

UltraCakeBakery commented Apr 19, 2021

An alternative route, partially based on @acoyfellow's approach is to leverage the ContextAPI for this:

<-- $layout.svelte -->
<script>
  import { setContext } from 'svelte';
  import { writable } from 'svelte/store';
  const layout = writable('layout1');
  setContext('setLayout', layout.set);
</script>

{#if $layout === 'layout1}....{/if}
<!-- dashboard.svelte -->
<script>
  import { getContext } from 'svelte';
  getContext('setLayout')('layout2');
</script>

The disadvantage of this is of course that you need to add some extra code to each page to set the layout, but you would have to do so anyway.

I praise the context API every time whenever I talk to my friends about Svelte, but using it to solve my problems is something I keep forgetting to do 😅. Thank you! This could actually work for now.

I wonder if this approach could be used by SvelteKit itself to provide a more out-of-the-box per route override option 🤔

@rmunn
Copy link
Contributor

rmunn commented Apr 19, 2021

Note that if the SSR works the way I think it will, when users go to /dashboard you'll be serving up a page that shows layout 1 first, then changes itself to layout 2 once dashboard.svelte's Javascript has been loaded. If you want to be safe, you might want to consider making layout 1 totally empty, and the other layouts contain actual HTML. That way you avoid having layout 1 flash on the page for a split second (kind of like a weird alternative version of a FOUC) before layout 2 shows up.

@UltraCakeBakery UltraCakeBakery changed the title Feature request: specify layout as module export Feature request: specify layout per route through module export Apr 19, 2021
@rdtect
Copy link

rdtect commented Apr 22, 2021

Since you cannot change the route names, Why not have the routes as folder routes instead and define a separate $layout.svelte file for each. Eg login/index.svelte and login/$layout.svelte would lead to a scoped layout for the login route.

(Maybe by importing the layout template from lib?)

No root level $layout.svelte for
Logout.svelte which can be left at root level.

Had the routes been a bit flexible, I would probably make the folder structures as per layout required. But that would mean changing the routes. So ./1/login or ./1/sign-up
And ./2/dashboard

I don't know if this helps but I usually use this simple technique,as I was used to routify. Speaking of which I agree that there should be a $reset.svelte for specific use cases like this.

@benmccann
Copy link
Member

Support for $reset.svelte was recently added

Anyway, I'm going to close this because I don't think it would be implemented as originally proposed

@UltraCakeBakery
Copy link
Author

UltraCakeBakery commented Apr 22, 2021

Since you cannot change the route names, Why not have the routes as folder routes instead and define a separate $layout.svelte file for each. Eg login/index.svelte and login/$layout.svelte would lead to a scoped layout for the login route.

(Maybe by importing the layout template from lib?)

No root level $layout.svelte for
Logout.svelte which can be left at root level.

Had the routes been a bit flexible, I would probably make the folder structures as per layout required. But that would mean changing the routes. So ./1/login or ./1/sign-up
And ./2/dashboard

I don't know if this helps but I usually use this simple technique,as I was used to routify. Speaking of which I agree that there should be a $reset.svelte for specific use cases like this.

The issue is that by importing it as a component and wrapping a page with it, is that the component itself will still get remounted on every page navigation. This makes any animation / transition in the component randomly cut-off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature / enhancement New feature or request router
Projects
None yet
Development

No branches or pull requests

6 participants