Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
Diagram illustrating the concept of LWC slots for building reusable Salesforce components effectively.
LWC

LWC Slots Guide - Building Reusable Salesforce Components

Tired of building rigid components that break when requirements change? Here is how I use LWC slots to create flexible placeholders that keep the code much easier to maintain.

The short answer

This guide covers unnamed and named slots in Lightning Web Components, which let a parent component inject its own markup into placeholders inside a child. It shows patterns for flexible, reusable UI layouts that do not need dozens of @api properties.

Key takeaways Use an unnamed slot as the catch-all placeholder when you build a reusable UI container component. Use named slots, targeted by a matching slot attribute in the parent, for multi-section layouts such as modals and cards. Check assignedElements() on the slot element in JavaScript so you can hide a wrapper instead of rendering empty white space. Put fallback markup inside the slot tags so the component still renders sensibly when nobody passes content.

If you're tired of building rigid components that break the moment a requirement changes, learn LWC slots. Slots are placeholders that let a parent component inject its own markup right into a child component, which makes your code far more flexible.

I've seen teams struggle with massive components carrying twenty different @api properties just to hide or show certain buttons. It's a maintenance nightmare. Rather than hard-coding every possible variation, I use slots and let the parent decide what goes where. You build a shell and the consumer fills in the blanks.

Why I always reach for LWC slots

Think of a slot as a "reserved seat" in your component's HTML. The child component defines where the seat is, and the parent component chooses who sits there. That helps a lot when you're trying to build a progress bar or a card that needs to look different depending on whether it's on a dashboard or a record page.

There are two types you'll use: unnamed (default) slots and named slots. An unnamed slot is the catch-all. If the parent passes content without a specific destination, it lands right there. Unnamed slots are the easiest way to start making your UI primitives reusable.

// cardComponent.html
<template>
  <div class="slds-card">
    <div class="slds-card__header">
      <h2>Standard Card</h2>
    </div>
    <div class="slds-card__body">
      <! - Anything the parent sends will show up here - >
      <slot></slot>
    </div>
  </div>
</template>

A default LWC slot: a component container receiving different UI elements into one central placeholder.

A default LWC slot: a component container receiving different UI elements into one central placeholder.

Taking control with named LWC slots

Sometimes a single placeholder isn't enough. If you're building something like a modal, you need specific spots for a header, a body, and a footer. That's what named slots are for. You give the slot a name attribute, and the parent targets it using the "slot" attribute on its own elements.

I use this pattern for almost every layout-heavy component I build. It keeps the CSS inside the child component consistent while giving the parent full control over the actual data and actions. It also makes LWC component communication much cleaner, because the parent keeps the logic for the buttons it's injecting.

// modalComponent.html
<template>
  <section class="slds-modal slds-fade-in-open">
    <div class="slds-modal__container">
      <header class="slds-modal__header">
        <slot name="header">
          <h2>Default Title</h2>
        </slot>
      </header>
      <div class="slds-modal__content">
        <slot></slot> <! - Default unnamed slot for body - >
      </div>
      <footer class="slds-modal__footer">
        <slot name="footer"></slot>
      </footer>
    </div>
  </section>
</template>

Checking if a slot is empty

One thing that trips people up is trying to hide a wrapper div when no content was passed to a slot. You can't just use an "if:true" on the slot itself. You have to go into the JavaScript and use assignedElements(). I've used this many times to hide a footer section entirely when the parent didn't provide any buttons, so the UI doesn't sit there looking clunky with empty white space.

// modalComponent.js
get hasFooter() {
  const footerSlot = this.template.querySelector('slot[name="footer"]');
  return footerSlot && footerSlot.assignedElements().length > 0;
}

Always provide default content inside your slot tags. It acts as a fallback so your component doesn't look broken if someone forgets to pass content to it.

Best practices from the field

  • Keep your slot names simple and descriptive like "header", "actions", or "sidebar".
  • Don't go overboard. If you find yourself adding ten slots to one component, you might actually need to break that component down into smaller pieces.
  • Remember that styles in the parent component don't automatically bleed into the child, but the content inside the slot is still technically part of the parent's DOM tree.
  • Use conditional rendering in the child to handle empty slots gracefully so you don't end up with weird margins or borders.

Key takeaways

  • Slots are the best way to handle content projection and build reusable UI primitives.
  • Named slots give you multiple injection points for complex layouts like modals or grids.
  • Slotted content stays in the parent's context, which makes event handling and styling much more intuitive.
  • Use assignedElements() in your JS if you need to detect whether a slot actually has content.

So the next time you're about to add yet another boolean property to a component just to show a specific icon or button, stop and try a slot instead. Your components get much easier to maintain, and nobody has to hunt through a mountain of props to change a bit of text.

Frequently asked questions

What is the difference between named and unnamed slots in LWC?

An unnamed slot is the catch-all: it receives any content the parent passes without a target attribute. A named slot has a `name` attribute, and the parent points markup at it with a matching slot attribute on its own elements.

How do you check if a slot is empty in LWC?

Query the slot element with `this.template.querySelector` and check `assignedElements().length` in JavaScript. When it comes back empty, hide the wrapper elements around the slot.

Why should you use LWC slots instead of @api properties?

A slot lets the parent inject custom markup straight into the child, so you do not have to maintain dozens of `@api` properties just to toggle UI elements. Layout and styling stay in the child while event handling and logic stay with the parent.

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment