NV Elements Catalog Starters Repo System Themes About Getting Started Changelog Metrics Support Accessibility Contributions Requests Migration Deprecations Integrations Installation MCP CLI Lint Angular Bundles Extensions Go Hugo Import Maps Lit NextJS Nuxt Preact React SolidJS Svelte TypeScript Vue Foundations Typography Iconography Themes Design Tokens Size & Space Objects Interactions Support Status Color Animation Fonts Layers Custom Layout Horizontal Vertical Grid Popovers i18n Visualization View Transitions Elements Accordion Alert Avatar Badge Breadcrumb Button Button Group Card Chat Message Checkbox Color Combobox Copy Button Datagrid Integrations Column Action Column Alignment Column Fixed Column width Container Card Display Settings Footer Heatmap Keynav Multi Select Pagination Panel Detail Panel Grid Performance Placeholder Row Action Row Groups Row Sort Scroll Height Single Select Stripe Date Datetime Dialog Divider Dot Drawer Dropdown Dropdown Group Dropzone File Forms Validation Actions Control Icon Icon Button Input Input Group Logo Menu Month Notification Page Page Header Page Loader Pagination Panel Progressive Filter Chip Progress Bar Progress Ring Password Preferences Input Pulse Radio Range Resize Handle Search Select Skeleton Sort Button Sparkline Star Rating Steps Switch Tabs Tag Textarea Time Toast Toggletip Toolbar Tooltip Tree Week Patterns Authentication Browse Dashboard Editor Empty States Heatmap Keyboard Shortcut Logging Media Onboarding Panel Responsive Search Subheader Trend Code Codeblock Monaco Input Diff Input Editor Diff Editor Problems Markdown Markdown CSS Utility Labs Responsive Layout Viewport Container Patterns Forms API Design Properties & Attributes Slots Registration CustomEvents Stateless Composition Styles Packaging Glossary Logs Internal Guidelines Agent Harness Documentation Examples TypeScript Testing Unit Testing Accessibility Testing Lighthouse Testing SSR Testing Visual Testing Troubleshooting Component Creation Internal Examples All Examples

Composition

API Inheritance - anti-pattern

Elements should default to using composition when possible. This approach is to maximize flexibility, compatibility, and API simplicity. API inheritance is when an API unknowingly or over time inherits or re-implements another element's API on its own to expose extra access to its otherwise internal implementation details. Example, using a button and icon element it's possible to start to see some of these tradeoffs.

Do Don't

    

    

It may be tempting to encapsulate other elements and expose their APIs to the host element. This can make the API seem more terse and less code to write. For example, if a button has an icon it may seem useful to provide an icon name API to set the button icon. But this quickly can run into API conflicts. First this conflicts with the native button API which has an existing name attribute so the developer must expose the icon via icon.

Going further this runs into layout conflicts. If the icon needs to change position the developer must add a secondary “escape hatch” API to change this behavior.

Do Don't

    

    

While this works it's introducing “escape hatch” APIs that the button now must support for the use of the icon. These situations can also complicate internationalization use cases where right-to-left languages reverse reading order and elements.

As the icon API grows over time so does the pressure for the button API to absorb and expose more API endpoints for the icon. This pressure is “API inheritance” which creates tightly coupled and non-explicit APIs that only exist for certain element usage/combinations. As a result, the API becomes more complex (and verbose) as it demands more escape hatches. In this example, the first API leads to the use of 67 chars vs the composition API at 65 chars. By leveraging composition and slots it's possible to avoid supporting escape hatch/tightly coupled APIs which long term keeps the supported API surface area smaller and easier to learn.

Default Slots and Customization

Elements should provide reasonable defaults for better developer experience for consumers of the library and the end user. Sometimes consumers need to customize the defaults themselves. For example, an alert message may show status types such as success, warning, and danger. Each may show a different icon within that box by default.


    

The alert can internally provide the default icon style for the status in the system. But as above with the button, the alert element runs the risk of absorbing parts of the icon API. To mitigate this, leveraging default slots can provide a hook for customizations.


    

Slots can provide default content if the consumer supplies no content. Here the template sets an internal icon with a status icon that matches the status of the alert. If the consumer wants to customize the icon, they can do so by projecting their own icon into the icon slot, overriding the default. This enables full control of the custom icon the consumer provides without the alert needing to expose the icon through a series of API inherited attributes/properties. As with all API choices there are tradeoffs.

Semantic Obfuscation - anti-pattern

When building composition based APIs the developer should push the semantics of the HTML up into the light DOM or the control of the consumer. In this example the card element embeds the h1 heading. This creates an incorrect DOM structure as only one given h1 can exist within the page. This also applies as the page structure should work down from h1-h6.

Do Don't

    

    

While composition based APIs may be more verbose at times, they lower the API surface area to learn in the system and help ensure there is a singular way to use the element. Once a consumer learns an element API, that API usage remains predictable and reliable throughout the system.

Consumer apps/plugins can add opinionated abstractions. This can provide a more opinionated terse API in which consumers can always “escape” or access the elements of the base library as needed. It's easier to add abstraction layers, it's much more difficult to pull apart the wrong base abstraction.