-
Notifications
You must be signed in to change notification settings - Fork 82
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
Conversation
I like this a lot. It's also similar to how it works in Angular (
|
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
It's just a regular |
I interpreted the question as 'how do we figure out what the types are' not 'what do I do with my keyboard' |
I meant "how do I add TypeScript typings". But I guess the simple answer for now is "it's type |
I can definitely get behind this.
A possible alternative to <svelte:template this="Image" let:src>...</svelte:template>
<Image {src} /> If we go with that route, it could make sense for Another option is to enforce the name be lowercased (eg, <svelte:template this="image" let:src>...</svelte:template>
<image {src}/> An obvious issue here with the lowercased approach is HTML/CE element conflicts. |
I had the need for something like this in Svelte Native. <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) |
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? |
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? <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} |
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 |
So a template is just like an each block, except all instances don't have to be in the one spot |
I think in general this proposal makes sense, but I'm not sure it's the best way to implement it. Few questions & remarks:
<template name="image" let:image>
<figure>
<img src={image.src}>
{#if image.caption}
<figcaption>{image.caption}</figcaption>
{/if}
</figure>
</template> Seems we already use
<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 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" /> |
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:
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 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. |
@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}} |
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 ? That said, having a lot of files can be a problem.
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. |
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. |
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. |
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: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} |
Having both 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 😄):
|
+1 for "partial." People know this nomenclature vs (confusing, overloaded) "template." |
Funny, for me it's the opposite. Having an Angular background, |
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. |
For reference |
That won't work because a partial can have slotted content. |
Thinking about it more is the inline component the |
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 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. |
@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. In general I have plans to do a review of the flow of things in svelte, type:
etc., - communication of everything with everything. 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 In fact, an inline component is equivalent to a function from Of course I'm not saying that every construct from script(and every other) has to be moved to markup and style etc. |
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. |
@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 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. |
Personally I have actually encountered more use cases for this than the |
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. |
Exactly; well said.
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 |
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? <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. |
Sorry I don't understand this comment at all, could you re-phrase it somehow?
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.
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.
@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. |
@kevmodrome Sorry, I can't. (ⴲ﹏ⴲ)/
The inline components syntax for <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 name () {
/* code */
} That is:
I'm not saying to create equivalents of every syntax from
I don't know if you understand more now? (ⴲ﹏ⴲ)/ |
It's not the only reason. Complexity in code bases, "glancability" and separation of concerns will all suffer if this was implemented.
I don't think this is an argument to include it at all. The feature should be able to stand on its own.
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 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. |
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.
@AradAral but in that case, wouldn't it make more sense to solve this in a preprocessor instead? Or even include it svelte-preprocess? |
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. |
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. |
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. |
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. |
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 Regardless, I want to push back on the thought that it's niche. |
Just FYI. Maybe somebody knows that Malina is marginal framework similar to Svelte. It have I like the definition of the |
In addition, in RactiveJS, it is called Partials. But I believe @Rich-Harris know about it ))) |
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:
|
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. |
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 I guess this would not allow for nested child elements... As of now, it is impossible to use the So what about changing Why use 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 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? |
Is this going to be implemented? Ran across the need myself |
@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. |
This is solved in Svelte 5 through snippets, therefore closing this RFC. |
Rendered