Svelte: Multiple components in one file

Created on 3 Jun 2019  路  17Comments  路  Source: sveltejs/svelte

What is this: This is a feature request

Description:
Sometimes, putting one component per one file is overkill and hard to maintain, especially when you have a lot of components. Use cases:

  • Styled components.
  • Private component (Component for local use only)

Allow multiple-component declarations in one file would help a lot.

Proposal Example:

design.svelte

{#def Input}
  <input class="input" {...$$props} />

  <style>
    .input {
      background: blue;
    }
  </style>
{/def}

{#def Button}
  <button class="button" {...$$props}>
    <slot></slot>
  </button>

  <style>
    .button {
      background: blue;
    }
  </style>
{/def}

app.svelte

<Button>Hi</Button>

<script>
  import { Button, Input } from './design.svelte'
</script>

Most helpful comment

Ok, after working with svelte for a while I'm _officially_ reversing my opinion on this (I commented above). Components are better off in a SINGLE file

I come from the Angular 2+ world where every component must be added to a module before being used in the parent component. This makes components very heavy weight as you need to change at _least_ 2 places in order to use a component.

Svelte components on the other hand are extremely light-weight and only need to be imported by the component that's using it! So there's no real gain in having it in the same file! except for the fact that it's a private component... which is not the best reason in my opinion

Single file components advantages:

  • Easier to maintain
  • Easier to reuse and copy
  • Easier to determine dependencies
  • Encourage simpler components

I'm sticking with single file components! :fist_raised:

All 17 comments

I like the idea about private components

While this would reduce file count on some projects, it would make the syntax of a svelte component/file a bit further from appearing to be just ordinary HTML CSS and JavaScript. It would add another layer of learning complexity, another speed bump to new users.

At the same time, "one file per component" is already substantially more dense and concise than is generally seen in some other SPA frameworks out there (at least if you follow their style guides). Perhaps one file per component is dense enough after all?

as a newbie to JS & Svelte, I'd like to add a voice here discouraging extra syntax and confusion for component definition. the reason Svelte is working so well for me is that it's really easy to understand what's happening, and where it's defined. Anything that adds complexity will increase the barriers to adoption. Thanks for listening :)

This isn't something we're interested in natively providing in the Svelte compiler. This sounds like something that could be implemented in, for example, a Rollup plugin without a tremendous amount of effort.

Personally I think Svelte should support multiple components per file. It's very common to need micro components (buttons, etc) for a single use in a view.

Svelte and Vue both have this single file component concept and personally I've found it quite impractical. What happens in practice is that instead of creating new files for these micro components these become part of the template and then the logic becomes a mess.

The fact there is no official solution for this problem is one of the reasons we won't be adopting Svelte. If this was solved in any way, it should be either included in the official compiler or some sort of official plugin/loader/etc. A third party solution to such a central problem would only introduce fragility.

All these comments are great, but it would be really great if Svelte could support some kind of _sub-components_ or _templates_.

For example in Angular (bear with me) they have a tag called <ng-template>, which is used to recreate things within the same component, kind of like a private subcomponent...

Would the native html <template> tag be an alternative? Or would that break browser compatibility or something?

I have to disagree with @PierBover and @benwinding - it's trivial to create another small file for sub-components - I do this myself, using a parent folder for the main component, and keeping my smaller (sub) compoents folder local to it.

I view the concept of multi-components in a file, and having some sort of extra magic tag as increasing complexity for no real reason.

I view the concept of multi-components in a file, and having some sort of extra magic tag as increasing complexity for no real reason.

It's not about magic and certainly not "for no real reason". Being able to create multiple components in the same file answers to very pragmatic reasons. It's not even a matter of opinion, in some situations it is objectively faster to work with multiple components in the same file.

Following your workflow of creating a folder. What if you created a component and later on you needed to add another component in it? Now you have to create a folder, move the component there, change your imports in other files, etc. Compare that to just adding another component to the same file which takes seconds.

Not only that but every time you want to work on that view you have to open all those little files in your editor instead of opening a single file. That again takes more time than just opening a single file.

we have over 2000 components in our react app. it's not possible to maintain 1500 different files. reducing the amount of files is a requirement for moving from react to selve.

@yinonc

Ok, after working with svelte for a while I'm _officially_ reversing my opinion on this (I commented above). Components are better off in a SINGLE file

I come from the Angular 2+ world where every component must be added to a module before being used in the parent component. This makes components very heavy weight as you need to change at _least_ 2 places in order to use a component.

Svelte components on the other hand are extremely light-weight and only need to be imported by the component that's using it! So there's no real gain in having it in the same file! except for the fact that it's a private component... which is not the best reason in my opinion

Single file components advantages:

  • Easier to maintain
  • Easier to reuse and copy
  • Easier to determine dependencies
  • Encourage simpler components

I'm sticking with single file components! :fist_raised:

kudos for publicly changing your mind :)

Easier to maintain

I guess it's very subjective but IMO less files and less folders makes projects much easier to navigate and reason about.

Encourage simpler components

I've experienced quite the opposite in Vue. I tended to created fat components since it's quite tedious to create a new component. OTOH it's also true that Vue 2 components have a lot more boilerplate than Svelte 3 components.

Another benefit of having multiple components in the same file is when you have a collection of related components together. It makes it super easy to implement changes in all of those and see how these are related together.

I was very pro Vue single file components and anti JSX a couple of years ago, but after using different libraries and frameworks with JSX on a couple of projects I changed my mind.

Anyway, I won't continue to beat a dead horse...

Cheers!

@clitetailor I know, this is not what you looking for, but maybe it would help a little bit:

./components/design/index.js

export { default as Button } from './components/Button.svelte';
export { default as Input } from './components/Input.svelte';

./components/design/components/Button.svelte

<button class="button" {...$$props}>
  <slot></slot>
</button>

<style>
  .button {
    background: blue;
}
</style>

Usage:

<Button>Hi</Button>

<script>
  import { Button, Input } from './components/design';
</script>

@PaulMaly, does this work OK with SSR in Sapper?

@saintech I believe it should.

I have to disagree with @PierBover and @benwinding - it's trivial to create another small file for sub-components - I do this myself, using a parent folder for the main component, and keeping my smaller (sub) compoents folder local to it.

I view the concept of multi-components in a file, and having some sort of extra magic tag as increasing complexity for no real reason.

awkwardly acceptable while trying to maintain a clean code structure.

the solution is <svelte:self>

here is the repl

<div
  class="svelte_self_recursion_demo {'comp_level_'+level}"
  class:main_component={type == 'main_component'}
  class:sub_component={type == 'sub_component'}
>
  {#if type == 'main_component'}
    <div>main_component<br/>lets include sub_component ....</div>
    <svelte:self type="sub_component" level={level + 1} />
  {:else if type == 'sub_component'}
    <div>sub_component on level {level}</div>
    {#if level < 4}
      <svelte:self type="sub_component" level={level + 1} />
    {/if}
  {/if}
</div>
<script>
  export let type = 'main_component';
  export let level = 0;
</script>
<style>
  .svelte_self_recursion_demo {
    border: solid 1px black;
    padding: 0.5em;
  }
  .main_component {  }
  .sub_component {  }
  .comp_level_0 { color: black; }
  .comp_level_1 { color: blue; }
  .comp_level_2 { color: green; }
  .comp_level_3 { color: red; }
  .comp_level_4 { color: yellow; }
</style>

edit: why the downvote? : P

i expect a compiler to optimize such dead code ....
assuming the dead code is harming performance

to optimize such code, we would need "set once" props, like

export init set_me_only_once;

proposing the new pseudo type init
behaving similar to the C preprocessor macros #define and #if

edit: we could require a list of constant values, like in a switch statement

export init set_me_only_once = ['preset_1', 'preset_2'];

currently, this is not optimized by svelte, but by rollup
(problem is, rollup is not aware of components?)

<script>
  let name = 'world';
  if (false) name = 'dead code js';
</script>
<h1>Hello {name}!</h1>
{#if false}<div>dead code html</div>{/if}

for my svelte-layout i need two large components that differ only in details
so im using a type prop to switch the component variant
to avoid maintaining two components with 90% same code
(no, i dont want to use a shared.js file)

svelte could optimize this with a space-time-tradeoff ("function unswitching")
by cloning the component for every init value
and optimize each subtype for its init value
yay component inheritance unswitching

maybe this can be implemented as a preprocessor
if svelte allows a preprocessor to create new components (*.svelte files)
and transform component calls like <C t="1"/> to <C_t_1/>

edit: this probably only makes sense
for deep recursion like {#if level < 2500} (unswitched)
or calling <svelte:self> like 50 000 times (unswitched)
cos hot code (like a mousemove handler) can be optimized already
and cold code optimization is an unnecessary/premature optimization

Was this page helpful?
0 / 5 - 0 ratings

Related issues

plumpNation picture plumpNation  路  3Comments

davidcallanan picture davidcallanan  路  3Comments

thoughtspile picture thoughtspile  路  3Comments

AntoninBeaufort picture AntoninBeaufort  路  3Comments

bestguy picture bestguy  路  3Comments