TL;DR it comes up to either the component's purpose is wrapping and styling its internals in reusable way, or providing some kind of specific behaviours inside.
I'm going to explain here in a bit more detail why is it so, without diving too deeply into boring ins and outs, and obvious pros and cons.
All the front-end web frameworks of our days have both props and slots concepts in their architecture, in one form or another. It's props and composition/containment in React, props and slots in Vue, inputs and content projection in Angular, and many other analogies around the modern web UI world. But the idea is common: you can either
- pass some data from a parent component to a child component (in form of primitive values or objects), or
- provide a way to "put" some parts of a parent components inside a single (or multiple, "named") "slot" of child component's template.
<!-- pseudocode -->
<parent-component>
<!-- props example -->
<child-component-one [someChildProp]="someValueFromParent" />
<!-- slots example -->
<child-component-two>
<p>{ anotherValueFromParent }</p>
</child-component-two>
</parent-component>
Both ways are mostly interchangeable. And, as usual, when we have two interchangeable (and rather opinionated) ways of doing things in programming, developers come to inevitable analysis paralysis.
IMHO, it's really important to know how these approaches work in your framework of choice. They give flexibility and power of handling the code reusability in a resilient way. But as usual, with great power comes great responsibility, and it means that you need to choose practically what to go with to avoid annoying refactoring chores in the future. In the worst cases some developers may mix the approaches, and it turns into maintenance nightmare for their peers.
After working with different frameworks and problems related to using slots vs props in UI components I had come to a rule of thumb for choosing one approach over another. And here it is:
Choose slots APIs when the component's main responsibility is providing reusable HTML/CSS block(s)/skeleton(s) without necessity to configure internal behaviours. Choose props APIs when you need to provide any interactivity inside the component, either you need to tweak its template/styles or not. Avoid mixing the two approaches and prefer decomposing the component to several subcomponents with separate responsibilities.
That's it. I intentionally try to avoid detailed examples here as they're too specific to the technology of choice. But if you have come to this writeup you probably know what I'm talking about, or you can click on the framework-specific links above to expand your knowledge.
Choose wisely, safe your intellectual power for business logic problems, and take care.
Top comments (3)
I find that in Vue TypeScript support for slots if much worse than for props. I use Vue 3 (and 2) with TSX and Vuetify, and always prefer to use props in my code over props. Due to Vuetify using slots I have to provide them for 3rd party components and in most cases they aren't properly typed.
Indeed, the argument about the type checking is totally legit. Though, as you mentioned, it always depends on a use case. Sometimes, as I say in the post, it all comes up to clean styles/layout scoping and for that, there’s no reason to be concerned with proper type validations.
Well, I agree. The biggest reason is to indicate that default slot is required in Vue, or children are required in React. In both cases IIRC default/children are typed as optional, so you have to do an ugly check inside of the component before rendering default/children to check they're not undefined... I think in React there might be a way to specify that children are required, haven't used it in a while, but in Vue it seems to be practically impossible. I guess it goes well with their ideology of checking everything runtime, like prop validation, but I prefer to check at compile time.