Meeting Users Where They Are: Layered Abstraction in Spotify's Encore

As design systems grow to serve larger, more varied organizations, the "this versus that" debate between customization and configuration quickly breaks down. Customization gives developers freedom and speed; configuration gives teams cohesion and maintainability. But when a company has thousands of engineers building everything from flagship consumer apps to internal tools, no single approach can satisfy everyone. Some teams have dedicated design support; many do not. Some features push the envelope of the UI; others are purely functional.

The answer for Spotify's Encore design system team has been to stop choosing. Instead, they offer multiple layers of abstraction simultaneously, allowing each team to work at the level that suits their specific task.

Three Touch Points on the Abstraction Spectrum

Encore's model centers on three distinct tiers of interaction with a component. The card component, one of the first built to explore this pattern, illustrates the differences clearly.

Abstraction spectrum for a card component.

Config: High-Level, Data-Driven Use

At the most abstract level, a user passes only data into the component's props. This is the standard case and offers the most convenience. The component handles all rendering logic, letting developers who simply need a standard card provide nothing but content. This is classic configuration mindset: hand over the data, and the system renders it in the best way it knows how.

Slots: Middle-Ground Modification

The second level is where slots come into play. Instead of adding props to a dense parent component to change its internals, users can pass in subcomponents directly. For example, a default card element might accept a string and render a standard image. But if a feature requires a differently shaped image or extra behavior like analytics, a developer can take that subcomponent, modify it, and pass it back into the card. Slots provide a transparent window into the subcomponent's own API, preventing feature-specific complexity from accumulating in the parent.

Custom: Full Control via Composition

For the most complex cases, Encore provides the base pieces and lets the customer build. Users can assemble the individual components themselves, managing subcomponents and styles on their own. This requires the most effort but offers the fewest constraints, ensuring the visual benefits of the design system don't become blockers for ambitious features.

The implementation of this pattern in Spotify's web stack relies on React and TypeScript. The fundamental win of this layered approach is that it broadens the baseline of what "standard" looks like. A design system's default can never meet every need, but with slots and custom composition, the system doesn't have to become rigid to stay cohesive.

Planning for the Future with Layered Defaults

Beyond immediate utility, Encore sees several strategic benefits to formalizing these layers of abstraction in its API.

Improved Utility and Innovation

Encore has historically leaned heavily toward customization, offering parts that users must assemble and maintain themselves. By investing in configuration layers—shipping accessible focus flows and uncontrolled component states—they avoid forcing every team to reinvent these essentials. The pattern ensures that adding defaults does not sacrifice flexibility. When basic implementations are quick, developers have more time to focus on what makes their feature unique.

Enhanced Reuse at the Repo Level

When a user passes a string into a slot, Encore renders a preset "default slot" subcomponent. This utility can be extended by the teams themselves. In the same way Encore handles generic UI, a product repo can handle specialized UI and functionality like routing or analytics. These project-specific details can be baked into a local component once, so day-to-day feature development only requires passing in simple data to get the correct, functionally-complete result.

Reduced Noise in Divergent Implementations

When a feature requires a truly unique component—such as a promotional card with special button styles—teams will inevitably build local versions. Having a broader baseline makes those local implementations cleaner. The parts that follow the standard can still be passed as simple strings, so the code clearly highlights only the logic that is genuinely distinct from the default.

Democratized Defaults Through Data

Custom slots aren't only useful to the teams who create them. By anonymizing analytics on slot usage, Encore can see which props are overridden most often and how. This data shows what customers actually need versus what the default provides. If a common custom variant overtakes the default in usage, that signals a candidate for a new default, keeping Encore in sync with how the product is truly being built.

For design system teams looking to generalize this approach, the inspiration is well documented in the broader community. Jake Wharton's writing on slope-intercept design breaks down how layering lowers the barrier for entry-level users while enabling power users. Nathan Curtis's guidance on subcomponents encourages maintainers to offer parts and let implementers compose. And Ben Fried's principle that the frequency of an action should dictate how well it is surfaced in an API maps neatly to the goal of making the most standard paths the easiest to follow.