Real-World Examples & Patterns in Web Components

Mikey NicholsMikey Nichols
14 min read

In today's diverse front-end landscape, Web Components stand out as a platform-native solution for creating reusable UI elements. While frameworks come and go, Web Components offer a standardized approach that works across codebases and technologies. But where can we see them in action? Who's using them in production? Let's explore the real-world implementation of Web Components and the patterns that have emerged as best practices.

Let's start with a basic example of a Web Component. This interactive demo shows the fundamental structure and lifecycle methods that make Web Components work. You can see how the component reacts when it's added to the DOM, removed, or when its attributes change:

Try clicking the buttons to see the lifecycle methods in action. This simple example demonstrates the core concepts of Web Components: custom elements, shadow DOM encapsulation, and lifecycle callbacks.

Who's Using Web Components Today?

Several major tech companies have embraced Web Components for their production applications:

Google

Google was an early champion of Web Components and continues to use them extensively:

  • YouTube: The YouTube web application uses Web Components for its video player interface, comments section, and recommendation panels.

  • Google Maps: Many of the interactive elements in Google Maps are built as custom elements.

  • Google Earth: The web version leverages Web Components for its UI controls.

Microsoft

Microsoft has significantly invested in Web Components:

  • Microsoft's Fluent UI: Their design system is implemented as Web Components to ensure consistent UIs across products.

  • Microsoft 365: Various applications within the suite use custom elements for shared functionality.

  • VS Code: Many interface elements are implemented as Web Components.

Adobe

Adobe has embraced Web Components for their design systems:

  • Adobe Spectrum: Their design system is available as Web Components through the Spectrum Web Components library.

  • Adobe Experience Cloud: Uses components from their Spectrum library across products.

Other Notable Examples

  • Salesforce: Their Lightning Web Components framework is built on the Web Components standards.

  • ING Bank: Redesigned their web applications using Web Components for consistent UX across platforms.

  • IBM: Carbon design system offers Web Components implementations.

  • GitHub: Uses Web Components for various UI elements across their platform.

  • Netflix: Has incorporated custom elements in parts of their streaming interface.

Libraries and Frameworks

Several libraries have emerged to simplify Web Component development:

  1. Lit: Google's lightweight library (successor to Polymer) has become the most popular choice for creating Web Components with minimal boilerplate.

  2. Stencil: Created by the Ionic team, Stencil generates highly optimized Web Components and can output framework-specific wrappers.

  3. FAST: Microsoft's adaptive UI library produces Web Components that automatically adapt to user preferences.

  4. Shoelace: A collection of professionally designed, every day UI components built on a framework-agnostic technology.

  5. Enhance: A framework that uses progressive enhancement to create multi-page applications with Web Components.

Hosting & Distribution Strategies

Companies distribute their Web Components in several ways:

  1. NPM Packages: The most common approach, allowing developers to install components via package managers.

    • Example: npm install @adobe/spectrum-web-components
  2. CDN Delivery: Pre-built components hosted on CDNs allow for quick inclusion without build steps.

    • Example: <script type="module" src="https://unpkg.com/@shoelace-style/shoelace@2.0.0/dist/shoelace.js"></script>
  3. Custom Element Registries: Specialized registries like webcomponents.org catalog available components.

  4. Bundled with Applications: Many applications bundle custom elements as part of their build process.

  5. Micro-frontend Architecture: Components distributed across multiple teams but united in a single application.

Common Patterns in Production Web Components

Composition Pattern

One of the most powerful patterns is component composition:

<!-- Container component -->
<user-profile>
  <!-- Composable child components -->
  <user-avatar slot="avatar"></user-avatar>
  <user-info slot="info"></user-info>
  <user-stats slot="stats"></user-stats>
</user-profile>

This pattern enables teams to build complex UIs from simpler building blocks, a strategy employed by many design systems.

To see the composition pattern in action, here's an interactive demo that demonstrates how slots distribute content in Web Components:

This card component uses three distinct slots (header, body, and footer) to create a flexible, reusable UI element. Try toggling different parts of the content to see how:

  1. The component maintains its structure even when slots are empty

  2. Content from the "light DOM" (your HTML) flows into the designated slots

  3. The component's styling remains consistent regardless of the content provided

  4. The theme can change while preserving the composition structure

  5. The code preview updates to show exactly what HTML you would write to achieve the current state

This pattern is especially powerful for design systems, as it allows components to maintain consistent styling and behavior while giving content authors flexibility to provide custom content for different sections.

Property/Attribute Pattern

Attributes for simple values, properties for complex data:

class DataTable extends HTMLElement {
  // Simple configuration via attributes
  static get observedAttributes() { 
    return ['loading', 'pagination']; 
  }

  // Complex data via properties
  set data(value) {
    this._data = value;
    this._renderTable();
  }
}

Google's Material Web Components and Adobe's Spectrum Web Components both follow this pattern.

To better understand how attributes and properties work together in Web Components, let's explore this interactive demo that clearly demonstrates their differences:

This demo shows how attributes are best for simple values that can be represented in HTML markup (strings, booleans), while properties excel at handling complex data like arrays and objects. Try using the controls to manipulate both attributes and properties, and observe how:

  1. Attribute changes trigger the attributeChangedCallback() and appear in the HTML markup

  2. Property changes don't affect the HTML markup but can handle complex data structures

  3. The component responds to both types of changes in different ways

This pattern is widely used in production Web Components. For example, Google's Material Web Components use attributes for styling options (like variant or dense), while reserving properties for data binding with complex values.

Event-Based Communication Pattern

Components communicate through custom events:

// Inside the component
this.dispatchEvent(new CustomEvent('item-selected', {
  bubbles: true,
  composed: true, // Crosses shadow DOM boundary
  detail: { id: selectedId }
}));

// Parent listening for events
document.querySelector('my-component')
  .addEventListener('item-selected', (e) => console.log(e.detail.id));

GitHub's web components utilize this pattern for their interactive elements.

Let's see events in action with this interactive demo. Here, two completely separate web components communicate through custom events without direct references to each other - a fundamental pattern for creating loosely coupled, reusable components:

In this example:

  • The color picker component dispatches a color-changed event when a color is selected

  • The event includes detailed data in the detail property (color code and name)

  • The display component listens for this event and updates accordingly

  • Notice how bubbles: true and composed: true allow the event to cross shadow DOM boundaries

API Consistency Pattern

Successful Web Component libraries establish consistent APIs:

// Consistent naming and behavior across components
<sl-button variant="primary" size="large" disabled></sl-button>
<sl-dropdown variant="primary" size="large" disabled></sl-dropdown>
<sl-checkbox variant="primary" size="large" disabled></sl-checkbox>

Microsoft's FAST components and Shoelace both exemplify this pattern with predictable property names.

Real-World Implementation Examples

Design System Components

Design systems often implement form controls with enhanced functionality:

class EnhancedSelect extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        :host {
          display: inline-block;
          position: relative;
          width: 200px;
        }
        .select-container {
          border: 1px solid var(--border-color, #ccc);
          border-radius: 4px;
          padding: 8px;
        }
        /* Additional styles... */
      </style>
      <div class="select-container">
        <slot name="selected-option"></slot>
        <div class="dropdown">
          <slot></slot>
        </div>
      </div>
    `;

    // Implementation details...
  }
}

customElements.define('enhanced-select', EnhancedSelect);

Now let's explore a fully functional implementation of the EnhancedSelect component that demonstrates how design system principles are applied in practice:

This enhanced select component showcases several key design system features:

  1. Theming - The component responds to design tokens for colors, spacing, and typography

  2. Consistent Visual Language - The styling follows design system patterns with proper spacing and visual hierarchy

  3. Accessibility - The component implements ARIA attributes, keyboard navigation, and focus management

  4. Configurability - Properties like density, size, and shape variants provide flexibility within the design system

  5. Component API - The interface follows consistent patterns with attributes and properties

Try changing the theme, density, and other properties to see how the component adapts while maintaining design system consistency. This pattern of using CSS custom properties as design tokens is used by libraries like Adobe's Spectrum Web Components and Microsoft's FAST.

Content Widgets

News sites and content platforms often use web components for interactive widgets:

class ContentRecommendation extends HTMLElement {
  static get observedAttributes() {
    return ['category', 'count'];
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (oldValue !== newValue) {
      this._fetchRecommendations();
    }
  }

  connectedCallback() {
    this._fetchRecommendations();
  }

  async _fetchRecommendations() {
    const category = this.getAttribute('category') || 'general';
    const count = parseInt(this.getAttribute('count') || '3', 10);

    try {
      const response = await fetch(`/api/recommendations?category=${category}&count=${count}`);
      const data = await response.json();
      this._renderRecommendations(data);
    } catch (error) {
      console.error('Failed to fetch recommendations:', error);
    }
  }

  _renderRecommendations(items) {
    // Render logic...
  }
}

customElements.define('content-recommendation', ContentRecommendation);

This pattern is used by sites like The Guardian and Washington Post for their recommendation modules.

Here's a working example of a content recommendation component that demonstrates the key concepts of content widgets in web components:

This demonstration shows how content-focused components:

  1. Fetch and display dynamic content - Simulating API calls to retrieve articles based on user selections

  2. Handle loading states - Using skeleton loaders to improve perceived performance

  3. Manage error conditions - Gracefully handling failures with clear user feedback

  4. Communicate via events - Dispatching custom events when articles are loaded or selected

  5. Respond to configuration changes - Updating content based on category and count selections

This pattern is commonly used by news sites, blogs, and content platforms to create modular, reusable recommendation widgets that can be placed throughout their applications.

This pattern is similar to what you'd find on content-heavy sites like The Guardian or The Washington Post, where web components encapsulate complex content widgets while maintaining clean integration with the surrounding application.

Performance Patterns

Lazy-Loading Components

Many production Web Components implement lazy-loading to improve performance:

// Only load the component when needed
document.querySelector('#show-comments').addEventListener('click', async () => {
  // Dynamically import the component
  await import('./components/comment-section.js');

  // Now create and use it
  const commentSection = document.createElement('comment-section');
  document.querySelector('#comments-container').appendChild(commentSection);
});

YouTube uses this pattern extensively to load UI components only when needed.

To demonstrate the power of lazy loading web components, here's an interactive example that simulates loading resource-intensive UI components only when needed:

This demo shows how lazy loading can dramatically improve initial page load performance by:

  1. Reducing initial page weight - Only the core 10KB of code loads initially

  2. Loading components on demand - Each component is loaded only when requested

  3. Providing visual feedback - The performance meter shows the impact of each loaded component

  4. Using dynamic imports - Components are loaded asynchronously without blocking the main thread

Try clicking the buttons to load different components and watch how the page weight increases only when new components are added. This pattern is used extensively by YouTube for their video player interface, where heavy components are loaded only when the user interacts with them.

Constructable Stylesheets

For components used multiple times on a page, shared stylesheets improve performance:

// Create once, use many times
const sheet = new CSSStyleSheet();
sheet.replaceSync(`
  :host {
    display: block;
    padding: 16px;
  }
  .content {
    color: var(--text-color, #333);
  }
`);

class ReusableCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    // Use the shared stylesheet
    shadow.adoptedStyleSheets = [sheet];
    shadow.innerHTML = `<div class="content"><slot></slot></div>`;
  }
}

Google's Material Web Components use this pattern for performance optimization.

To better understand the performance benefits of Constructable Stylesheets, this interactive demo demonstrates the difference between traditional inline styles (duplicated for each component) and shared stylesheets (reused across all components):

Try adding multiple components with both methods to see how Constructable Stylesheets significantly reduce memory usage and improve performance as the number of components increases. This technique is especially valuable in applications like YouTube, where dozens or hundreds of component instances might appear on a single page.

Accessibility in Production Web Components

Successful Web Components in production prioritize accessibility:

class AccessibleTabPanel extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <div class="tabs" role="tablist">
        <slot name="tab"></slot>
      </div>
      <div class="panels">
        <slot name="panel"></slot>
      </div>
    `;

    // Add keyboard navigation, ARIA attributes, etc.
  }

  connectedCallback() {
    // Set up accessibility requirements
    const tabs = this.querySelectorAll('[slot="tab"]');
    const panels = this.querySelectorAll('[slot="panel"]');

    tabs.forEach((tab, index) => {
      // Set ARIA attributes
      tab.setAttribute('role', 'tab');
      tab.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
      tab.setAttribute('tabindex', index === 0 ? '0' : '-1');
      tab.setAttribute('aria-controls', `panel-${index}`);

      // Set panel attributes
      panels[index].setAttribute('role', 'tabpanel');
      panels[index].setAttribute('id', `panel-${index}`);
      panels[index].setAttribute('aria-labelledby', tab.id);
      panels[index].hidden = index !== 0;

      // Add event listeners for keyboard navigation
      // ...
    });
  }
}

Adobe's Spectrum Web Components and Microsoft's FAST components both implement comprehensive accessibility features like this.

Accessibility is a crucial aspect of web components. Let's explore an accessible tab panel component that demonstrates proper keyboard navigation and ARIA attributes:

This accessible tab panel implementation showcases several key accessibility features:

  1. Proper ARIA roles and attributes - Using role="tablist", role="tab", and role="tabpanel" with appropriate relationships

  2. Keyboard navigation - Arrow keys to move between tabs, Home/End to jump to first/last tab

  3. Focus management - Visible focus indicators and proper focus order

  4. Screen reader support - Appropriate announcements when tabs change

Try navigating the tabs using only your keyboard. Press Tab to focus on the tab list, then use arrow keys to move between tabs. Notice how the focus is visually indicated and how the selected tab's content is displayed.

This implementation follows the WAI-ARIA Authoring Practices for tab panels, ensuring that users of assistive technologies can effectively interact with the component.

Integration with Frameworks

Many organizations need to integrate Web Components with existing framework codebases:

React Integration

// React wrapper around a Web Component
import React, { useRef, useEffect } from 'react';
import 'my-web-components/dist/data-chart.js';

function DataChartWrapper(props) {
  const chartRef = useRef(null);

  useEffect(() => {
    // Pass complex data as properties
    if (chartRef.current) {
      chartRef.current.data = props.data;
    }
  }, [props.data]);

  // Pass simple values as attributes
  return (
    <data-chart
      ref={chartRef}
      theme={props.theme}
      height={props.height}
      onChartUpdate={props.onUpdate}
    />
  );
}

Salesforce uses this pattern to integrate their Lightning Web Components with React applications.

To see how React and Web Components work together in practice, here's an interactive demo showing the integration patterns in action:

This demo showcases the key integration patterns described earlier:

  1. Using refs to access Web Component properties - React uses a ref to directly set properties on the Web Component

  2. Event-based communication - The Web Component dispatches custom events that React listens for

  3. Bidirectional data flow - Changes in either React or the Web Component are synchronized

Try changing the color or clicking the buttons on either side to see how React and the Web Component communicate. Notice how property changes flow from React to the Web Component, while events flow from the Web Component back to React.

Micro-Frontend Architectures with Web Components

Organizations like ING Bank have implemented micro-frontend architectures using Web Components:

<!-- Shell application -->
<html>
<head>
  <title>Banking Dashboard</title>
  <!-- Import shared components -->
  <script type="module" src="/shared/components/ing-header.js"></script>
  <script type="module" src="/shared/components/ing-footer.js"></script>
</head>
<body>
  <!-- Shared header component owned by core team -->
  <ing-header user-id="12345"></ing-header>

  <!-- Micro-frontend container -->
  <main>
    <!-- Accounts micro-frontend owned by accounts team -->
    <accounts-overview></accounts-overview>

    <!-- Investments micro-frontend owned by investments team -->
    <investments-summary></investments-summary>

    <!-- Loans micro-frontend owned by loans team -->
    <loans-overview></loans-overview>
  </main>

  <!-- Shared footer component -->
  <ing-footer></ing-footer>

  <!-- Load micro-frontends -->
  <script type="module" src="/accounts/components/accounts-overview.js"></script>
  <script type="module" src="/investments/components/investments-summary.js"></script>
  <script type="module" src="/loans/components/loans-overview.js"></script>
</body>
</html>

This approach allows different teams to develop and deploy independently while maintaining a cohesive user experience.

Let's visualize how a micro-frontend architecture might work in practice with this interactive demo:

This simplified banking portal demonstrates the core principles of micro-frontend architecture:

  1. Team Ownership Separation - Each colored section is owned by a different team

  2. Independent Development - Each team can build and deploy their component independently

  3. Cross-Team Communication - Components interact through a centralized event bus

  4. Consistent User Experience - Despite being developed separately, components provide a unified experience

Try interacting with different components and watch the event log to see how they communicate. When you click on an account in the Accounts section, notice how the Support widget responds with contextual information - demonstrating cross-component communication.

This pattern is similar to ING Bank's approach, where different teams own distinct parts of the application while maintaining a cohesive user experience through standardized design systems and communication protocols.

Based on real-world adoptions, several trends are emerging:

  1. Server-Side Rendering with Declarative Shadow DOM - Companies like Salesforce and Google are exploring SSR for Web Components.

  2. Web Components in Design Systems - More organizations are standardizing their design systems as Web Components.

  3. Framework Integration Tools - Better tools for seamless integration with React, Angular, and Vue are being developed.

  4. Performance Optimizations - Techniques like Constructable Stylesheets and lazy-loading are becoming standard practice.

  5. Web Component Marketplaces - More robust ecosystems for sharing and discovering components are emerging.

Conclusion

Web Components have moved beyond theory into practical, production-ready solutions embraced by major tech companies. The patterns established by these implementations provide valuable blueprints for organizations looking to create scalable, maintainable component libraries that work across frameworks and technologies.

By examining real-world examples from Google, Microsoft, Adobe, and others, we can see that Web Components excel in scenarios requiring reusability, framework-agnosticism, and long-term stability. Whether you're building a design system, implementing micro-frontends, or simply creating UI widgets, these production-tested patterns can guide your implementation.

As browser support and tooling continue to improve, we can expect Web Components to become an even more essential part of the web development ecosystem.

Resources

0
Subscribe to my newsletter

Read articles from Mikey Nichols directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mikey Nichols
Mikey Nichols

I am an aspiring web developer on a mission to kick down the door into tech. Join me as I take the essential steps toward this goal and hopefully inspire others to do the same!