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

Feature Suggestion: Improve versatility of <svelte:component> and <svelte:element> by treating them as a <svelte:fragment> when the this prop value is present but is a falsy or nullish value #7437

Closed
brandonmcconnell opened this issue Apr 8, 2022 · 10 comments

Comments

@brandonmcconnell
Copy link
Contributor

brandonmcconnell commented Apr 8, 2022

Describe the problem

In some cases, I would like to conditionally render some content in a component or element.

Describe the proposed solution

For these cases, it would be useful if we were able to conditionally omit the component or element by using a nullish or falsy value in the this prop. So this example (below) would simply render Some text since the this prop is a falsy value:

<svelte:component this={false && MyComponent}>
  Some text
</svelte:component>

In such cases, the <svelte:component> or <svelte:element> would simply be treated as a <svelte:fragment>.

Alternatives considered

The main alternative is see is similar but essentially creating my own Fragment component which simply returns the slotted content in this format:

<slot {...$$restProps} />

In practice, it could be used like this:

<svelte:component
  this={condition
    ? WrapperComponent
    : Fragment
  }
>
  Some text
</svelte:component>

The downside to this, aside from having to import another custom component is that it would not work at all for <svelte:element>. In those cases, I would actually have to create a Svelte component counterpart for every single element so I could use them against my new Fragment component like this:

<script>
  import { Fragment, Div } from './customComponents.js';
  const condition = Math.random() < 0.5;
</script>

<svelte:component
  this={condition
    ? Div
    : Fragment
  }
>
  Some text
</svelte:component>

Importance

would make my life easier

@lukaszpolowczyk
Copy link

I second this, as I suggested something similar: #6898 (comment)
In my case it would be this="svelte:fragment". :)

@dimfeld
Copy link
Contributor

dimfeld commented Apr 16, 2022

Rendering the slot by itself when this is falsy would be a breaking change in Svelte. Less important perhaps for svelte:element since it's new but svelte:component has been around for a long time. Something like a Fragment component seems fine for that case.

For svelte:element, I like @lukaszpolowczyk's idea of using "svelte:fragment" to trigger the "render the slot with no wrapping element" behavior.

@AlbertMarashi
Copy link

AlbertMarashi commented Apr 16, 2022

@dimfeld I believe part of the purpose of svelte:element is to be able to bind to a dynamically created element within svelte.

I am curious why you can't use an #if block to do this, as it's only two possibilities, as compared to dozens of HTML elements

@dimfeld
Copy link
Contributor

dimfeld commented Apr 16, 2022

Contrived example and you could definitely make a component to make it easier to manage, but basically this:

{#if element}
  <svelte:element this={element}>
    500 lines of markup
  </svelte:element>
{:else}
  same 500 lines of markup
{/if}

@brandonmcconnell
Copy link
Contributor Author

Rendering the slot by itself when this is falsy would be a breaking change in Svelte. Less important perhaps for svelte:element since it's new but svelte:component has been around for a long time. Something like a Fragment component seems fine for that case.

For svelte:element, I like @lukaszpolowczyk's idea of using "svelte:fragment" to trigger the "render the slot with no wrapping element" behavior.

I may be mistaken here, but wouldn't it constitute as a breaking change if this change were to break projects that are currently working?

I believe this change would do the opposite; it would not break any working codebases, but it would cause Svelte to see this={condition && MyComponent}, as valid where it presently throws an error in such cases (when the returned value is falsy or nullish). Any codebases that currently employ this syntax would be potentially erroneous already; this change would change that to make it an acceptable syntax.

(Again, I may very we'll be mistaken here ☝🏼)

Yes, I also really like @lukaszpolowczyk's idea of being able to use svelte:fragment as the this value, and I think it could be valuable if supported for both svelte:component as well as the new svelte:element. I opened another feature request related to this specifically (#7396), but it was closed with the reasoning that svelte:fragment is not an actual variable or class that can be referenced at runtime, though I'd think the compiler could be adjusted to accommodate for a change like this. This issue birthed from that issue's resolution.

@dimfeld
Copy link
Contributor

dimfeld commented Apr 16, 2022

The current (and officially documented) behavior of svelte:component is to render nothing if this is falsy. See https://svelte.dev/repl/654a4be7e34e4aec81a6fb48c5e9a30c?version=3.47.0

@brandonmcconnell
Copy link
Contributor Author

brandonmcconnell commented Apr 18, 2022

Great call-out, @dimfeld. I was mistaken here and thought I had seen a similar example throw an error.

My suggestion would then be to support svelte:fragment per my previous request (#7396), and @lukaszpolowczyk's suggestion in #6898.

Seeing as those were closed and this current suggestion would be a breaking change, I will close this ticket. Thanks, all. 👋🏼

@ghost
Copy link

ghost commented Apr 22, 2022

I agree there should be a way of conditionally rendering some content in a component or element with a single tag. I'm currently using the svelte component below to handle this, but a built-in solution would be preferable.

<script>
  export { this_ as this }

  let this_
</script>

{#if this_ === '#'}
  <slot />
{:else if typeof this_ === 'string'}
  <svelte:element this={this_} {...$$restProps}>
    <slot />
  </svelte:element>
{:else}
  <svelte:component this={this_} {...$$restProps}>
    <slot />
  </svelte:component>
{/if}

@janosh
Copy link
Contributor

janosh commented Jan 3, 2023

Curious if anyone has an argument against

<svelte:element this="svelte:fragment" /> == <svelte:fragment />

@Rich-Harris
Copy link
Member

We definitely don't want to do this, it would be a confusing change in behaviour. In Svelte 5, we have snippets which provide much more control over this sort of thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants