DEV Community

loading...
Cover image for πŸš€ Svelte Quick Tip: Styling conditional named slots

πŸš€ Svelte Quick Tip: Styling conditional named slots

Dana Woodman
Software hacker working in the industry since 2003. Currently loves: #Typescript and #Svelte. Founder of Chimera, the first makerspace in northern California (https://www.chimeraarts.org/).
・Updated on ・4 min read

πŸ‘‹ Hello, World!

In my previous post, I described how you could selectively style the children of a <slot>. Now, we're going to zoom out a bit and see how we can style the <slot> containers themselves.

You'll likely find yourself doing this when you're working with multiple name slots that also happen to be optional.

As far as I'm aware, there are 3 basic ways we can do this:

  1. Style the wrapped component
  2. Style based on the [slot="..."] name
  3. Use conditional slots

Let's go into each now using the example of a <Modal> component with an optional actions slot for form actions:

Impatient? The tl;dr is to use conditional slots πŸ€“


1. Style the wrapped component

Likely the most basic of the bunch, we can wrap a slot with a tag and then apply styles to that parent tag:

<section>
  <div>
    <slot />
  </div>
  <footer>
    <slot name="actions" />
  </footer>
</section>

<style lang="postcss">
  section {
    background: white;
    border-radius: 1rem;
    box-shadow: 0 0.4rem 0.5rem rgba(0, 0, 0, 0.15);
  }
  div {
    padding: 2rem;
  }
  div :global(> :first-child) {
    margin-top: 0;
  }
  div :global(> :last-child) {
    margin-bottom: 0;
  }
  footer {
    background: #ececec;
    border-bottom-left-radius: 1rem;
    border-bottom-right-radius: 1rem;
    padding: 1.25rem 2rem;
  }
</style>

Enter fullscreen mode Exit fullscreen mode

We can then use this component like so:

<h1>With slot content</h1>
<Modal>
  <p>Hello, World!</p>
  <div slot="actions">
    <button>Click me</button>
  </div>
</Modal>

<h1>Without slot content</h1>
<Modal>
  <p>Hello, World!</p>
</Modal>
Enter fullscreen mode Exit fullscreen mode

The problematic part about this solution is that if the slot does not have content, it will still display which is of course not what we really want:

example 1

This happens because the <footer> tag exists in the DOM and thus has its styling applied so we see the gray box instead of nothing as we expect.

I assume this is most peoples first approach when making named slots (as it was mine) so read on for better solutions if that is your story too!

See this in the Svelte REPL here.


2. Style based on the [slot="..."] name

The only difference here is that we're goin to style the slot using just the slot name as a selector. To do this, first remove the wrapping <footer> tag from the slot:

<section>
  <div>
    <slot></slot>
  </div>
- <footer>
-    <slot name="actions"></slot>
-  </footer>
+  <slot name="actions"></slot>
</section>
Enter fullscreen mode Exit fullscreen mode

Next, we will use the [slot="..."] syntax combined with the :global modifier to target the slot by its name. This way we can style the slot without adding extra markup:

-footer {
+section :global([slot="actions"]) {
  /* ...styling remains the same... */
}
Enter fullscreen mode Exit fullscreen mode

You can now see that the footer is hidden like we expect:

example 2

In fact, since the slot gets no content passed to it, it is never even rendered to the DOM:

example 2 rendered html

This particular approach is useful when you need to directly style the slot itself and can be used in combination with conditional slots below.

See this in the Svelte REPL here.


3. Use conditional slots

This is probably the "right" way to solve this problem in most cases since it doesn't require any weird CSS wizardry like the previous example.

We just simply wrap the slot in a conditional that will cause it to only render the slot and its content if the slot was given content in the consuming component:

{#if $$slots.actions}
  <footer>
    <slot name="actions"></slot>
  </footer>
{/if}
Enter fullscreen mode Exit fullscreen mode

The $$slots prop is a prop that Svelte gives all components which just represents a dictionary of named slots (learn more about $$slots here).

The reason I consider this the "right" way to solve this problem is that no markup is rendered to the browser and you don't need to do the sorta hacky :global modifications like example #2.

In addition, you could add transitions/animations when it is mounted like you can other Svelte elements, which is pretty neat 😻

See this in the Svelte REPL here.


🎬 Fin

Well, that's all folk! 🐰

Hopefully this gives you a bit more clarity on dealing with styling of named slots in Svelte!

In summary, you'll want to go with conditional slots for most use cases as it is the cleanest solution of the three and still allows you to apply specific styling/structure to the slot itself.


Have other tips, ideas, feedback or corrections? Let me know in the comments! πŸ™‹β€β™‚οΈ

Don't forget to follow me on Dev.to (danawoodman), Twitter (@danawoodman) and/or Github (danawoodman)!

Photo by Joshua Aragon on Unsplash

Discussion (0)