Skip to content
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

Inline components #34

Closed
wants to merge 3 commits into from
Closed

Inline components #34

wants to merge 3 commits into from

Conversation

Rich-Harris
Copy link
Member

@dummdidumm
Copy link
Member

dummdidumm commented Sep 10, 2020

I like this a lot. It's also similar to how it works in Angular (ng-template) but easier to grasp. My notes:

  • Agree that it should not allow for a <script>. This is when you really should starting extracting the code into a component.
  • About the name thing: this feels more consistent at first but the semantics are different, so I would say no to this - unless we change the semantics and <svelte:template bind:this={bla} means you need a variable bla in the script which can then be referenced; but I guess this is too verbose. Alternative: We could add special syntax for it, like #thisIsTheName. This is how Angular does it. My vote goes to name in the end.
  • Template scope: They should have access to all variables/methods inside the component, but their style should be independent.
  • Top level only or not: I'm split on that one. Top level only would make components more readable (and I guess easier to implement in Svelte core). But I'm not sure if we would lose possibilities. One thing (from a typing perspective) that comes to my mind is stuff like "I'm inside this #if so I know that variable x is defined and I can safely use it in my template". TypeScript would otherwise error at you "hey this is possibly undefined". But I guess this could be solved by redefining x as a let:y.
  • Speaking of types: How to type let:y on the templates?

@Rich-Harris
Copy link
Member Author

Template scope: They should have access to all variables/methods inside the component, but their style should be independent.

Can you elaborate on this? Are you saying that in this case...

<script>
  const answer = 42;
</script>

<svelte:template>
  <p>the answer is {answer}</p>
</svelte:template>

<p>some red text</p>

<style>
  p {
    color: red;
  }
</style>

...it would print the answer is 42 but it wouldn't be red? I think I'd find that surprising — I'd expect either complete encapsulation (great for portability, though maybe a bit finicky for common cases) or for styles and state to cross the barrier.

Speaking of types: How to type let:y on the templates?

It's just a regular let:, so you can do let:x={y} if you need to rename something. (Or did I misunderstand the question?)

@Conduitry
Copy link
Member

I interpreted the question as 'how do we figure out what the types are' not 'what do I do with my keyboard'

@dummdidumm
Copy link
Member

I meant "how do I add TypeScript typings". But I guess the simple answer for now is "it's type any".

@lukeed lukeed mentioned this pull request Sep 10, 2020
@lukeed
Copy link
Member

lukeed commented Sep 10, 2020

I can definitely get behind this.

  • Should be defined a top-level only
  • Would prefer this= for both <svelte:component> and <svelte:template>
  • inherits everything because it was defined in that context:
    • local <style> overrides styles
    • no <script> to avoid complexity
    • use {#const} to define local var(s)

A possible alternative to <svelte:component this="image"/> would be something like this:

<svelte:template this="Image" let:src>...</svelte:template>

<Image {src} />

If we go with that route, it could make sense for Image to be fully isolated (inherits nothing) since it looks like any other component. This requires that the name ("Image") be capitalized as a linter/compiler rule.

Another option is to enforce the name be lowercased (eg, "image"), and is used as such:

<svelte:template this="image" let:src>...</svelte:template>
<image {src}/>

An obvious issue here with the lowercased approach is HTML/CE element conflicts.
A secondary issue (to both) is that it might be pretty tricky to get ESLint/TS tooling to figure out Image came from this="Image" -- but I've no idea.

@lukeed lukeed mentioned this pull request Sep 10, 2020
@halfnelson
Copy link

I had the need for something like this in Svelte Native.
The nativescript ListView control takes a "template" as a slot and instantiates it. I went with something based on this
https://svelte.dev/repl/d8c056f37ce949d7a7d1be5afe9cac53?version=3.25.0

<script>
	import AsComponent from './AsComponent.svelte'
	let menuItem
</script>

<AsComponent bind:component="{menuItem}" let:props={{val}}>
    <h3>This could be a  menu item {val}</h3>
</AsComponent>

<svelte:component this={menuItem} val="1" />
<svelte:component this={menuItem} val="2" />
<svelte:component this={menuItem} val="3" />
<svelte:component this={menuItem} val="4" />

Not ideal, but there was a nice advantage to having the component be a full svelte component. It allowed me to pass instances around and instantiate them outside of the component or pass them into children as props (although we have slots for this)

@rob-balfre
Copy link

I like it.

Side note... Would this allow tree shaking for parts of a component not in use? For example svelte-select has loads of features (too many TBH!) and I'd love to allow parts of it to be excluded from the compiled output if not in use. So if parts of svelte-select were rewritten as inline components and linked to props and those props are not used then rollup (no example) would not compile and output that code?

@knobo
Copy link

knobo commented Sep 11, 2020

What happens if you alter an input value in an inline template

<svelte:template this="Image" let:src>
      <img on:click={() => src="/img/clicked.png"} {src} />
</svelte:template> 

Is it a good idea or not?

Are there done any considerations on if input properties should be const?
And how would dispatch work if there no inline scripts?
Could it be passed in as a prop somehow?

<svelte:template this="Image" const:src  dispatch>
      <img on:click={() => dispatch('notify', 'detail value')} {src} />
</svelte:template> 

If it does not get its own event dispatcher, where would the event end up? On the main component's parent or on the inline tag.

{#each images as image, index}
 <Image src={image} on:notify={() => doSomething(images, index) }>
{/each}

@dummdidumm
Copy link
Member

I would say the answer to these questions is "the template shares the scope with the parent, so it can both read and write values defined in <script>".

@halfnelson
Copy link

So a template is just like an each block, except all instances don't have to be in the one spot

@PaulMaly
Copy link

PaulMaly commented Sep 11, 2020

I think in general this proposal makes sense, but I'm not sure it's the best way to implement it. Few questions & remarks:

  • I don't think the inline component (partial / template) should have its own styles and state. I believe it should work completely in the context of the SFC.
  • I think we can just <template> tag for this purpose:
<template name="image" let:image>
  <figure>
    <img src={image.src}>
    {#if image.caption}
      <figcaption>{image.caption}</figcaption>
    {/if}
  </figure>
</template>

Seems we already use <slot> tag in a specific svelte-way.

  • I'm not sure we should change the behavior of <svelte:component>, it's confusing me a little. Maybe we could jointly consider another proposal #2324 which is very popular and already has a PR. And just extend <svelte:element /> idea and give the ability to pass template element to that tag with just more wide capabilities.
<template name="image" let:image>
  <figure>
    <img src={image.src}>
    {#if image.caption}
      <figcaption>{image.caption}</figcaption>
    {/if}
  </figure>
</template>

{#each images as image}
  {#if image.link}
    <a href={image.link}>
      <svelte:element this="image" {image} />
    </a>
  {:else}
     <svelte:element this="image" {image} />
  {/if}
{/each}
  • I think we should avoid any context separation of the inline component and the component which contains it. It would be better if content inside <template> tag would be related to state & styles almost the same as <slot> tag.
  • I think inline component is not the best term for this and confusing. I prefer partial template - but you want to know so here we go.

@AlexxNB
Copy link

AlexxNB commented Sep 11, 2020

I prefer an alternative version in the RFC(partialy). In my opinion, inline-component declaration should be in control block(like each, if, await and others), but its using in the template should be as element. I suggested similar syntax for slots also in the past.

{#template:Button text,type}
    <button class:primary={type=='primary'}>{text}</button>
{/template}

...

<Button text="Click me" type="primary" />
<!-- OR -->
<svelte:element this="Button"  text="Click me" type="primary" />
<!-- OR -->
<template:Button   text="Click me" type="primary" />

@PierBover
Copy link

To be honest, I think I'm one of the people that motivated this feature by complaining on Twitter and Github. :)

IMO this is going in the right direction. Obviously we will never have the flexibility of JSX or HS with a SFC format, and that's totally fine. Logic-less pieces of templates are a fine solution and would solve 80% of the problem.

Although I think it would be nice to refine and simplify this even further. I agree with @PaulMaly here:

I think inline component is not the best term for this and confusing. I prefer partial template - but you want to know so here we go.

Since these inline components do not have their own logic, I don't think these can be called components at all. We'd still need to write event handlers and whatnot in the <script> of the file. Wouldn't it be more consistent to do the same with styles and just call these template partials or fragments?

It would be easier to understand and explain too. Instead of thinking about it like a component but with exceptions, it's just a piece of template that can be reused in the file.

@PaulMaly
Copy link

@AlexxNB Just to be sure that we all know how it was in Ractive - In-template partials

{{#partial image}}
  <figure>
    <img src="{{src}}">
    {{#if caption}}
      <figcaption>{{caption}}</figcaption>
    {{/if}}
  </figure>
{{/partial}}

{{#each images}}
  {{#if link}}
    <a href="{{link}}">
      {{> image }}
    </a>
  {{else}}
     {{> image }}
  {{/if}}
{{/each}}

@stephane-vanraes
Copy link

IMO this is going in the right direction. Obviously we will never have the flexibility of JSX or HS with a SFC format, and that's totally fine. Logic-less pieces of templates are a fine solution and would solve 80% of the problem.

I would say the exact opposite: this is a move in the wrong direction.

In JSX I often find issue with opening a component (say House) and having to dig through all kind of code related to Room and Roof before getting to the actual code for the House I am looking for. Even if those components are not used elsewhere, to me it doesn't make sense to have them inlined at all. It also feels like a violation of the Single Responsibility principle: my House.svelte should only concern how to render a house, not how the individual rooms are dealt with.

On top of that, how does a solution like this handle testing ?
In JSX it's easy to test the 'inline templates': just export them as well ! (completely undoing the reason why you inlined them in the first place)
In order to make these inline templates testable (and one day people will ask for it) a method of exposing them will have to established as well, this in turn will open the door to having files simply filled with templates that are exported in real components.

That said, having a lot of files can be a problem.
But the problem does not lay in file management (that can be easily solved by using good naming, folder structures, etc.
For instance the following structure still allows me to simple do import House from './House:

- /House
      - index.svelte
      - Room.svelte
      - Roof.svelte

The problem with having lots of 'dumb' files is the extra overhead created from having so many components in the compiled code, bringing the code closer to the inflection point where the generated bundle is larger than alternative frameworks.

@PierBover
Copy link

PierBover commented Sep 11, 2020

In order to make these inline templates testable (and one day people will ask for it) a method of exposing them will have to established as well, this in turn will open the door to having files simply filled with templates that are exported in real components.

If you consider these as template partials instead of inline components then these are just part of the same component. It would follow the same model as one component per file, SRP, etc.

When testing React you don't really test a piece of JSX in a variable inside a component.

@pngwn
Copy link
Member

pngwn commented Sep 11, 2020

Yeah. We don't want people testing inline components as they are implementation details. And we don't not want to encourage the testing of implementation details.

@pngwn
Copy link
Member

pngwn commented Sep 11, 2020

I think Svelte can only go so far when encouraging good practices, like any other library. For example, I consider deep folder structures an anti-pattern than negatively impacts discoverability and makes onboarding very challenging.

I think inline components solve certain issues and could add a degree of flexibility that we don't currently have. People could certainly over-use them but people also currently write incredibly large components that are difficult to understand too, it could be argued that Svelte encourages that as well.

I think some of the reasons for this centre around the cost of additional components, and confusion over how Svelte scales. In most cases these are misconceptions but there is some truth in them. We could do better when it comes to highlighting that scaling is not much of an issue in practice but I also think additional flexibility in certain cases is valuable to users. We aren't throwing all of our principles away here, just suggesting the reuse of inline fragments.

@Rich-Harris Building on a conversation we had a while ago about fragments, what about using svelte:fragment instead:

<svelte:template name="image" let:image>
  <figure>
    <img src={image.src}>
    {#if image.caption}
      <figcaption>{image.caption}</figcaption>
    {/if}
  </figure>
</svelte:template>

{#each images as image}
  {#if image.link}
    <a href={image.link}>
      <svelte:fragment this="image" {image}>
    </a>
  {:else}
    <svelte:fragment this="image" {image}>
  {/if}
{/each}

@benmccann
Copy link
Member

Having both svelte:template and svelte:fragment might be more concepts than we need.

E.g. could we say that if it has inner content it's defining the template and if it doesn't then it's using it (switching names here just for fun 😄):

<svelte:partial name="image" let:image>
  <figure>
    <img src={image.src}>
    {#if image.caption}
      <figcaption>{image.caption}</figcaption>
    {/if}
  </figure>
</svelte:partial>

{#each images as image}
  {#if image.link}
    <a href={image.link}>
      <svelte:partial this="image" {image}>
    </a>
  {:else}
    <svelte:partial this="image" {image}>
  {/if}
{/each}

@arxpoetica
Copy link
Member

arxpoetica commented Sep 11, 2020

+1 for "partial." People know this nomenclature vs (confusing, overloaded) "template."

@dummdidumm
Copy link
Member

dummdidumm commented Sep 11, 2020

Funny, for me it's the opposite. Having an Angular background, template feels natural to me. Also it fits the intention better.

@PierBover
Copy link

people also currently write incredibly large components that are difficult to understand too, it could be argued that Svelte encourages that as well

I agree the SFC model encourages fat components. I've seen this over and over in Vue projects that use SFCs and it's really a "pick your poison" type of situation. In complicated views you either end up working over a dozen files, or you end up having a messy fat component.

Inline components / partials / fragments / whatever would help bring some order into fat components without blocking people that prefer to work with multiple files.

@pngwn
Copy link
Member

pngwn commented Sep 11, 2020

For reference svelte:fragment. If I recall this was one of the reasons for the fragment rename, so we could use it in other places (this being one of the suggestions). So it wouldn't be new, although that PR is still open, there is broad agreement that the PR should be merged at some point.

@pngwn
Copy link
Member

pngwn commented Sep 11, 2020

@benmccann

could we say that if it has inner content it's defining the template and if it doesn't then it's using it (switching names here just for fun

That won't work because a partial can have slotted content.

@pngwn
Copy link
Member

pngwn commented Sep 11, 2020

Thinking about it more is the inline component the fragment in this case? Maybe that makes more sense.

@PierBover
Copy link

PierBover commented Jan 13, 2022

I have to admit I feel less strongly about this as I did when I started using Svelte two years ago.

Objectively, I still think JSX is more flexible and there are definite advantages to this flexibility like being able to create multiple components in single file.

But OTOH I now also think that adding multiple components in a single .svelte file would be quite inelegant, regardless of the implementation. I've come to appreciate that Svelte's simplicity and elegance are really its biggest assets (less code, less abstractions, less cognitive load, etc).

IMHO Svelte should trend towards an even simpler and more elegant workflow. Just like we saw with the recent inclusion of the style properties which will save us a lot of mangling with strings for dynamic styles.

@lukaszpolowczyk
Copy link

@kevmodrome @PierBover It pains me that someone can write like that at all.

I understand that it is expensive to maintain the "extra" syntax - if that is the ONLY reason, then ok.

But I will never agree with the statement that inline component or similar contructions are unnecessary or somehow hurt.

See if inline component is not analogous to e.g. @const?
After all, inline component is just making you not have to write the same SHORT markup twice.

In general I have plans to do a review of the flow of things in svelte, type:

svelte-script -> svelte-script
svelte-script -> svelte-markup
svelte-script -> svelte-style
svelte-markup -> svelte-script
svelte-markup -> svelte-markup
svelte-markup -> svelte-style
svelte-style -> svelte-script
svelte-style -> svelte-markup
svelte-style -> svelte-style

etc., - communication of everything with everything.
It is about passing simple data, as well as whole blocks of "body" of a given type(script, markup, style).

E.g. there are also some shortcomings in communication with style. But I will not write everything here.

In this thread we have the issue of using the markup "body", that is the communication svelte-markup -> svelte-markup.

In fact, an inline component is equivalent to a function from svelte-script - with svelte-script the body is the code in the function, with svelte-markup the body is the markup block.

Of course I'm not saying that every construct from script(and every other) has to be moved to markup and style etc.
But I'm certainly saying that there should be constructs for communication/transfer/use of body/simple data between script-markup-style.

@stephane-vanraes
Copy link

This is a feature that is often enough requested to at least be considered.

That said I am not a proponent either as I am very much in favour of the 'Single File Component' principle Svelte has had up to now.

Since the comparison with JSX came up, I will also say that this is the very first thing I actually advice clients when using React: remove inline components and stick to one component per file, the result has always been the same: easier to read, maintain and debug code. This with the added benefit that in JSX it is clear where one inline component starts and where it ends because of the function signature, something that might be less clear with Svelte.

All in all, with the API surface of Svelte growing it means conflicting views on how to use it will emerge, with this comes that eventually teams will have to agree internally on coding practices to ensure consistency of their code base.

This means some teams might allow inline components (not mine) and others will not (mine), just like usage of features in other frameworks can be limited by an organization.

But this is a good thing, it shows Svelte is flexible and mature, able to change to different environments and practices.

@aradalvand
Copy link

aradalvand commented Jan 14, 2022

We discussed this RFC on Svelte Radio today (not released yet) and I'm getting more and more skeptical towards this.

@kevmodrome I strongly disagree. There are perfectly legitimate use cases for this, cases where creating a separate component would be too much, such as the one I mentioned in this comment. And there are use cases as well, for instance, when you have a piece of markup that's specific to a single component (so it doesn't make sense to extract it into a separate component) but it's repeated more than once in your component.
I totally agree with @lukaszpolowczyk:

See if inline component is not analogous to e.g. @const?
After all, inline component is just making you not have to write the same SHORT markup twice.

I don't understand the reason for you getting more skeptical about this?! It doesn't add much complexity, there are legitimate use cases for it, and people who don't want to use it will not have to.

@ponderingexistence
Copy link

ponderingexistence commented Jan 14, 2022

Personally I have actually encountered more use cases for this than the {@const ...} thing.
I am very much in favor of it. I don't think it has any major cons (apart from some extra complexity) but it enables very useful scenarios.

@madeleineostoja
Copy link

I'd agree that multi-component files go against the philosophy of Svelte, and I think they could encourage problematic edge cases especially since Svelte templates don't have a single root node.

However I do think that defining reusable snippets of markup — whatever you want to call it (templates, consts, fragments) — has a lot of value and doesn't muddy the waters. You're still living in the same component instance, all you're doing is DRYing out markup, which feels very much in line with Svelte's philosophy. If one doesn't already exist I think a separate RFC should be opened for that specific proposal.

@aradalvand
Copy link

aradalvand commented Jan 14, 2022

However I do think that defining reusable snippets of markup — whatever you want to call it (templates, consts, fragments) — has a lot of value and doesn't muddy the waters. You're still living in the same component instance, all you're doing is DRYing out markup, which feels very much in line with Svelte's philosophy.

Exactly; well said.

If one doesn't already exist I think a separate RFC should be opened for that specific proposal.

I think this current RFC is essentially the same concept, the name is what's a little misleading here, because these aren't really "inline components" but rather, as you said, component-level reusable bits of markup.

I'd suggest we call this something else — like "templates", which also matches the <svelte:template> tag, or "partials" — but again, the underlying construct is perfectly legitimate.

@aradalvand
Copy link

aradalvand commented Jan 14, 2022

Okay wait a second, one thing I'm struggling to understand even after reading the proposal though is whether or not an "inline component" (or template or whatever) would have direct access to the variables declared in its containing component?
I was assuming it would, of course; but I'm now confused. Would the following work, for example?

<script>
    let someVariable = 'Hello, World!';
</script>

<svelte:template name="foo">
    {someVariable}
</svelte:template>

<svelte:component this="foo" />

If not, then there would be little benefit to this approach, over separate components, and I would probably have to create another proposal that includes this capability as I don't think is really what I was talking about. But if anybody knows the answer to my question, please let me know.

@kevmodrome
Copy link

kevmodrome commented Jan 14, 2022

@kevmodrome @PierBover It pains me that someone can write like that at all.

I understand that it is expensive to maintain the "extra" syntax - if that is the ONLY reason, then ok.

But I will never agree with the statement that inline component or similar contructions are unnecessary or somehow hurt.

See if inline component is not analogous to e.g. @const? After all, inline component is just making you not have to write the same SHORT markup twice.

In general I have plans to do a review of the flow of things in svelte, type:

svelte-script -> svelte-script
svelte-script -> svelte-markup
svelte-script -> svelte-style
svelte-markup -> svelte-script
svelte-markup -> svelte-markup
svelte-markup -> svelte-style
svelte-style -> svelte-script
svelte-style -> svelte-markup
svelte-style -> svelte-style

etc., - communication of everything with everything. It is about passing simple data, as well as whole blocks of "body" of a given type(script, markup, style).

E.g. there are also some shortcomings in communication with style. But I will not write everything here.

In this thread we have the issue of using the markup "body", that is the communication svelte-markup -> svelte-markup.

In fact, an inline component is equivalent to a function from svelte-script - with svelte-script the body is the code in the function, with svelte-markup the body is the markup block.

Of course I'm not saying that every construct from script(and every other) has to be moved to markup and style etc. But I'm certainly saying that there should be constructs for communication/transfer/use of body/simple data between script-markup-style.

Sorry I don't understand this comment at all, could you re-phrase it somehow?

We discussed this RFC on Svelte Radio today (not released yet) and I'm getting more and more skeptical towards this.

@kevmodrome I strongly disagree. There are perfectly legitimate use cases for this, cases where creating a separate component would be too much, such as the one I mentioned in this comment. And there are use cases as well, for instance, when you have a piece of markup that's specific to a single component (so it doesn't make sense to extract it into a separate component) but it's repeated more than once in your component. I totally agree with @lukaszpolowczyk:

See if inline component is not analogous to e.g. @const?
After all, inline component is just making you not have to write the same SHORT markup twice.

I have never said that there aren't use-cases, I'm saying they are niche use cases. There aren't enough of them to justify the complexity of this. And to be clear, when I'm talking about complexity I'm not only talking about the internals - I'm worried we'll see a departure from easily digestible, understandable code.

I don't understand the reason for you getting more skeptical about this?! It doesn't add much complexity, there are legitimate use cases for it, and people who don't want to use it will not have to.

This is not a good argument. Much like Svelte is opinionated on the styling issue that I know you're very passionate about, it can be opinionated on other things as well.

I'd agree that multi-component files go against the philosophy of Svelte, and I think they could encourage problematic edge cases especially since Svelte templates don't have a single root node.

However I do think that defining reusable snippets of markup — whatever you want to call it (templates, consts, fragments) — has a lot of value and doesn't muddy the waters. You're still living in the same component instance, all you're doing is DRYing out markup, which feels very much in line with Svelte's philosophy. If one doesn't already exist I think a separate RFC should be opened for that specific proposal.

@AradAral @madeleineostoja I'd love to get some real concrete use-cases for this as well as the frequency at which you encounter them. In those cases, would creating a new file really be that much work? From the way you argue about this it sounds like it's something you're running into all day and all night. I think I've had to deal with this a handful of times over the course of 3 years.

@lukaszpolowczyk
Copy link

Sorry I don't understand this comment at all, could you re-phrase it somehow?

@kevmodrome Sorry, I can't. (ⴲ﹏ⴲ)/


  1. If the only reason to abandon inline components is the cost of maintaining Svelte, then I understand the problem.
    The other reasons for abandoning inline components I don't understand. I recognize that the other reasons for abandoning inline components are false.

  1. @const is similar to inline components.
    Thanks to it, you don't have to write a piece of code many times.
    If we have entered @const, all the more reason to enter inline components.

  1. Svelte is divided into:
  • svelte-script - code
  • svelte-markup - tags like div, span etc.
  • svelte-style - css

The inline components syntax for svelte-markup, is equivalent to the function syntax for svelte-script, e.g. inline component:

<svelte:template name="tooltip" let:dangerousness>
  {@const danger = dangerousness > 5}

  <div class="tooltip" class:danger>
    <slot></slot>
  </div>
</svelte:template>

is equivalent to, for example. function:

function name () {
  /* code */
}

That is:

  • You create a function and use in the second and third place.
  • You create an inline component and use it in the second and third place.

  1. Both function and inline component are very basic syntax:
  • What is between <svelte:template name="tooltip" let:dangerousness>
    and </svelte:template>, is svelte-markup-body (that's what I called it, you can give a better name).
  • What is between function name () { and } , is svelte-script-body.

I'm not saying to create equivalents of every syntax from svelte-script to svelte-markup etc, but the ability to reuse svelte-markup-body as well as svelte-script-body is abosolutely basic syntax.


  1. I also wrote that it would be useful to do a comprehensive review of reuse, syntax portability, and communication between svelte-script, svelte-markup, and svelte-style.
    But that's a broader topic, I just mentioned it.

I don't know if you understand more now? (ⴲ﹏ⴲ)/

@kevmodrome
Copy link

  1. If the only reason to abandon inline components is the cost of maintaining Svelte, then I understand the problem.
    The other reasons for abandoning inline components I don't understand. I recognize that the other reasons for abandoning inline components are false.

It's not the only reason. Complexity in code bases, "glancability" and separation of concerns will all suffer if this was implemented.

  1. @const is similar to inline components.
    Thanks to it, you don't have to write a piece of code many times.
    If we have entered @const, all the more reason to enter inline components.

I don't think this is an argument to include it at all. The feature should be able to stand on its own.

  1. Svelte is divided into:
    ...

I think I see where you're coming from here, though I don't find the argument particularly convincing. Besides, you can already re-use svelte-markup-body, it's called make a new component. One of the worst parts of React is the ability to stick an arbitrary number of components (functions, I suppose) in one file. I would very much like to avoid that in Svelte.

I don't think anyone has sufficiently showed that there are enough situations where this feature is needed to warrant implementing it. Especially with regards to the downsides already mentioned.

@PierBover
Copy link

Just to be clear, I'm not opposed to implementing inline templates or fragments.

I don't like having duplicated markup but I've found this is not a common issue for me (once I tamed my DRY obsession from JSX). It's not even close to being one of my biggest pain points when using Svelte.

I think this current RFC is essentially the same concept, the name is what's a little misleading here, because these aren't really "inline components" but rather, as you said, component-level reusable bits of markup.

@AradAral but in that case, wouldn't it make more sense to solve this in a preprocessor instead? Or even include it svelte-preprocess?

@ryanatkn
Copy link

ryanatkn commented Jan 14, 2022

I agree it's a fairly niche need. At the same time, many developers work in niches. The energy behind the PR suggests that it addresses a real pain point, more than a theoretical or aesthetic preference, but 1) we're stuck with anecdotes, 2) every example has its workarounds, and 3) how much pain is it to how many people?

I subjectively rate it as desirable probably higher than most people, but I'd also understand if the RFC was rejected. The feature adds significant complexity and costs, and breaks expectations that some people find important. Is it worth the pain relief? That question doesn't have one answer.

@madeleineostoja
Copy link

madeleineostoja commented Jan 17, 2022

I would also agree that this isn't a huge pain point, but a nice-to-have. At the end of the day we're talking about convenience and cleanliness here, it's not like having reusable bits of markup would make something previously impossible suddenly possible, it's just an ergonomics thing. But a huge part of Svelte in DX, so I do think it's worth considering, even if it's not a high priority in Svelte's roadmap.

I don't have examples on hand, I've probably run into situations I would have reached for this feature maybe half a dozen times in my own work (typically CMS-backed websites where I would have previously used NextJS) over the last 6 months as well. Basically wherever I would have previously thrown a chunk of JSX in a const. Again none of these cases were a dealbreaker just having to repeat some markup, it would've just been nice.

Given that (for me at least) it's a DX thing, I would reiterate that the complexity and unforseen edge cases of having legitimate inline components would not be worth it right now. I'm just talking about bits of markup that ideally have access to the containing components props.

@ryanatkn
Copy link

ryanatkn commented Jan 17, 2022

I'll voice a niche usecase that I have only some experience with: using Svelte to "draw" with a bunch of HTML or SVG elements, like algorithmic art/graphics. For example see the Twitter hashtags #creativecoding and #generative: https://twitter.com/search?q=%23creativecoding%20OR%20%23generative%20&f=top (I hesitate to use this example because of recent developments in the digital art world, but it's the best example I know of; ignore any related hashtags you dislike)

The idea here is that you're putting arbitrary stuff onscreen, feeling your way around with props/params that do mathy/algorithmic things, completely unlike normal business apps. In these situations it's common to have multiple reusable bits of ad-hoc logic/markup. Most stuff in the wild is probably generated with functions on a canvas, not a template language like Svelte, but Svelte works well for many of these things too, and extracting every reusable bit into a component is a burden.

@eden-omb
Copy link

eden-omb commented Feb 5, 2022

As a yet-additional use case, Storybook is currently quite annoying to work with in Svelte if you're using more than one template. I think about inline components as just private top-level components for internal use inside a given component. If there's cause for them to be scoped and not unique within a component (as the RFC asks) I believe it's only reasonable for those bits to be extracted to a different component.

As for context and styling, since these are for innercomponental use only I believe they should be inherited. This could likely be done with a preprocessor as @PierBover suggests. Reusable bits of markup inside a template would be massively helpful in simplifying templates and reducing duplication.

I will point out that this is very easy to do is something like JSX where you can simply keep bits of markup in variables and render them wherever you want, and being able to do that purely inside the template is an important core feature to have I believe. I think this should be built into Svelte as an internal processor.

@arxpoetica
Copy link
Member

I don't believe it's niche. I run into the need all the time. I think I'll have to remember to come comment on this, however, since—at the moment—I can't seem to pull them out of my head.

One thing that is vaguely coming to my memory is use cases where {#if...}...{/if} blocks get unnecessarily complex to include or exclude nested HTML that has to be repeated more than once. Usually, in those use cases, building a new component is annoying. This is sufficiently vague to not be a useful comment, so, like I said, I'll try and remember to come back to this.

Regardless, I want to push back on the thought that it's niche.

@AlexxNB
Copy link

AlexxNB commented Feb 8, 2022

Just FYI. Maybe somebody knows that Malina is marginal framework similar to Svelte. It have fragments, a some sort of inline components which we discussing here.
When creating UI-lib for this framework I used fragments many times. They allow me to avoid code repeating and leave template in current component. Separating template into child component is not logical here and it increase the bundle.
https://github.com/AlexxNB/malina-ui/blob/master/components/Form/Button.xht
https://github.com/AlexxNB/malina-ui/blob/master/components/Form/Input.xht

I like the definition of the fragments by author of Malina's - 'It is a function, but for template'.

@PaulMaly
Copy link

PaulMaly commented Feb 8, 2022

In addition, in RactiveJS, it is called Partials. But I believe @Rich-Harris know about it )))

@brunnerh
Copy link
Member

Just opened an issue about a feature like this and was pointed here. For me a key point would be to get access to the component class for its use with the client-side component API. This aids in interoperability with non-svelte libraries/components.

My main suggestions summarized:

  • Reuse <svelte:fragment> for the definitions
  • let:... behaves like a property (export let ...)
  • bind:this={Component} binds the component class to a local variable (in this case Component; has to be declared with let)
    • Allows use with client-side API (new Component({ target: ... }))
    • Allows use with <svelte:component this={Component}>
  • name="Component" would allow direct use with a regular tag: <Component />

@sken130
Copy link

sken130 commented Nov 4, 2022

Saying this feature is not needed is like saying Java should not have inner classes and C# should not have nested classes.

Even only 5% of people have encountered valid use cases for inline components, it's already enough to make it a valid feature.

Also, when persuading other developers and management to adopt a new framework like Svelte instead of React, sometimes, a single downside (apart from ecosystem being small) is enough for them to turn you down, despite having so many other advantages. And the lack of inline components is one of such downsides. This features may look small, but if you need it, then it is a critical feature. I want to use Svelte, but I don't know what argument I could make up for the lack for this feature.

@KevsRepos
Copy link

KevsRepos commented Dec 21, 2022

I just want to mention, that this:

<script>
	let importantMessage;
</script>

<template bind:this={importantMessage}>
	<h1>Cake</h1>
</template>

{@html importantMessage?.innerHTML}

is already possible. https://svelte.dev/repl/f8ebf7109afa4608a604f04bc13b640b?version=3.55.0
And I would leave it like that. Basically. We could implement syntactic sugar for this line: {@html importantMessage?.innerHTML} like {@template importantMessage}

I guess this would not allow for nested child elements... As of now, it is impossible to use the slot prop in <template> elements as Svelte hijacked the slot mechanism as we all know and errors out, if we try to use it in the original native way.

So what about changing {@template importantMessage} to <svelte:template></svelte:template> and allowing slots inside template tags to be used the way they were intended to use.

Why use <template> instead of svelte:*?
"Use the platform" - and stick as near as possible to the platform. Svelte has always taken credit for feeling like "native html and javascript", though I couldnt agree less, not hijacking the meanings of the <slot> element and not bringing in another framework specific element or changing the meanings of already existing framework specifc elements, would actually make Svelte feel more like the native way of doing things.
Why bring in our own way of doing things, when html actually has a neat syntax already?

EDIT: Additionally I want to say that basically Svelte's strength is hiding the DOM API behind very semantic, declarative and neat syntax. So let us think the other way around: html and javascript already give us everything we need. Its only the DOM API which would require us the template.cloneNode() and so on. So its this specific part that we need to hide. And with <svelte:template>, we simply hide the implementation of cloneNode and so on.

I dont think we need scoped styles for templates. CSS classes are a thing! I think leaving the styles scoped to the document is enough. And eh voila, we kinda have reusable chunks of html.

Though I agree we might need a little bit more dynamic stuff here. So far, I could'nt find a solution to add the content of a template element with dynamic properties. It seems nearly impossible to do so currently in a reasonably usable way.

So what about adding a syntax to allow this? This is especially difficult to determine how syntax for that should look like. Any ideas?

@space-nuko
Copy link

Is this going to be implemented? Ran across the need myself

@pngwn
Copy link
Member

pngwn commented Mar 28, 2023

@space-nuko the verdict is still out but not before the next major version. We'll need to set aside some time to go through the RFCs prior to planning the next version.

@dummdidumm
Copy link
Member

This is solved in Svelte 5 through snippets, therefore closing this RFC.

@dummdidumm dummdidumm closed this Nov 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.