Shoelace: Build Web Components for React Applications

A comprehensive guide to integrating framework-independent web components into your React projects with first-class TypeScript support and testing strategies.

What is Shoelace?

Shoelace is a collection of professionally designed, framework-independent web components that provide essential UI functionality for modern web applications. Built on the browser's native Custom Elements API, Shoelace components work seamlessly across frameworks, making them particularly valuable for teams working in polyglot environments or planning framework migrations.

Why Use Web Components in React?

Web components offer several advantages for React applications:

  • Framework Independence: Use the same components across React, Vue, Angular, and other frameworks
  • Browser-Native Performance: Leverage the browser's native custom elements implementation
  • Accessibility Built-In: All components meet WCAG accessibility standards
  • Consistent Design Language: Professional UI components with a unified design system
  • Future-Proofing: Built on web standards that will continue to be supported by browsers

For teams building custom software solutions that may span multiple frameworks, Shoelace provides an excellent foundation for maintaining UI consistency while allowing each application to use its preferred technology stack. When implementing a design system, consider pairing Shoelace with established design system tools like Radix to create a cohesive component architecture that scales across your organization.

Installation Methods

Choose the approach that best fits your project needs

CDN Installation

Quickest way to get started. No build configuration required. Ideal for prototyping and testing components.

npm Installation

Recommended for production applications. Enables tree-shaking and optimal bundle sizes with your bundler.

Cherry Picking

Import only the components you need. Reduces bundle size while maintaining full functionality.

TypeScript Support

Full type definitions included. Better developer experience with autocomplete and type checking.

CDN Installation (Quick Start)

The fastest way to try Shoelace in a React project is through CDN-based installation, which requires no build configuration. This approach works well for prototyping, testing components, or small projects where bundle size is less critical.

<!-- Include a theme -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/themes/light.css" />

<!-- Load the autoloader -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/shoelace-autoloader.js"></script>

The autoloader automatically registers components when they appear in the DOM, even if added dynamically after initial page load.

npm Installation (Production)

For production React applications, npm installation with a bundler provides optimal performance through tree-shaking and code splitting.

npm install @shoelace-style/shoelace

When using npm, you must configure the base path for assets (icons, images) using the setBasePath() utility:

import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
setBasePath('/path/to/shoelace/dist');

This approach is particularly valuable for enterprise web application development where bundle optimization directly impacts user experience and hosting costs. Combined with CSS flexbox layouts and responsive design principles, you can create applications that load quickly and perform reliably across all devices.

Using Shoelace Components in React

Component Imports and Usage

Shoelace provides React-specific versions of every component for idiomatic usage in React applications. These React components offer better integration with React's rendering and state management:

import SlButton from '@shoelace-style/shoelace/dist/react/button/index.js';
import SlInput from '@shoelace-style/shoelace/dist/react/input/index.js';
import SlCard from '@shoelace-style/shoelace/dist/react/card/index.js';

function MyComponent() {
 return (
 <SlCard>
 <h2>Welcome</h2>
 <SlInput label="Name" placeholder="Enter your name" />
 <SlButton variant="primary">Submit</SlButton>
 </SlCard>
 );
}

Event Handling in React

Shoelace components emit custom events using the sl- prefix. In React, you handle these events using the onSlEventName pattern:

import { useState } from 'react';
import SlInput from '@shoelace-style/shoelace/dist/react/input/index.js';

function LoginForm() {
 const [email, setEmail] = useState('');

 return (
 <SlInput
 type="email"
 label="Email"
 value={email}
 onSlInput={(event) => setEmail(event.target.value)}
 />
 );
}

TypeScript Integration

TypeScript support is built into Shoelace's React components:

import { useState } from 'react';
import SlInput, { type SlInputEvent } from '@shoelace-style/shoelace/dist/react/input/index.js';
import type SlInputElement from '@shoelace-style/shoelace/dist/components/input/input.js';

function ContactForm() {
 const [phone, setPhone] = useState('');

 const handleInput = (event: SlInputEvent) => {
 setPhone((event.target as SlInputElement).value);
 };

 return (
 <SlInput
 type="tel"
 label="Phone Number"
 value={phone}
 onSlInput={handleInput}
 />
 );
}

These patterns ensure type safety and excellent developer experience when building React-based applications with Shoelace components. For more advanced React patterns, explore our guides on testing React hooks and animation libraries to enhance your component development workflow.

Testing Shoelace Components

Jest Configuration

Testing web components in Jest requires additional configuration because Jest runs in Node.js without a full browser environment. Several browser APIs need mocking for Shoelace components to work correctly in tests.

// setupTests.js
Object.defineProperty(window, 'matchMedia', {
 writable: true,
 value: jest.fn().mockImplementation(query => ({
 matches: false,
 media: query,
 onchange: null,
 addListener: jest.fn(),
 removeListener: jest.fn(),
 addEventListener: jest.fn(),
 removeEventListener: jest.fn(),
 dispatchEvent: jest.fn(),
 })),
});

Testing React Components with Shoelace

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import MyForm from './MyForm';

test('updates form state on input', async () => {
 const user = userEvent.setup();
 render(<MyForm />);

 const input = await screen.findByLabelText('Email');
 await user.type(input, '[email protected]');

 expect(input).toHaveValue('[email protected]');
});

Implementing comprehensive testing strategies is essential for maintaining quality in enterprise software development. Proper test coverage ensures that web component integrations remain stable as your application evolves. Combined with proper TypeScript dependency injection patterns, you can build robust, maintainable applications that scale effectively.

Common Questions

Best Practices and Patterns

Form Integration

Shoelace form components support standard form integration through the name attribute:

function SignupForm() {
 const handleSubmit = (event) => {
 event.preventDefault();
 const formData = new FormData(event.currentTarget);
 const data = Object.fromEntries(formData);
 console.log('Form data:', data);
 };

 return (
 <form onSubmit={handleSubmit}>
 <SlInput name="username" label="Username" required />
 <SlInput name="email" type="email" label="Email" required />
 <SlInput name="password" type="password" label="Password" required />
 <SlButton type="submit" variant="primary">Sign Up</SlButton>
 </form>
 );
}

Performance Considerations

  • Use explicit imports instead of the autoloader for predictable bundle sizes
  • Implement code splitting by dynamically importing Shoelace components only when needed
  • Consider lazy loading Shoelace components with React.lazy for large applications

When to Use Shoelace in React Projects

Shoelace is particularly valuable for:

  • Multi-framework applications: Sharing UI components across React and other frameworks
  • Framework migration: Maintaining UI consistency during gradual migration to another framework
  • Design system implementation: Building a design system that must work across multiple projects
  • Long-term projects: Avoiding dependency on a specific React component library

For teams building scalable web applications, using framework-independent components like Shoelace can significantly reduce technical debt and provide flexibility for future platform evolution. This approach aligns well with creating comprehensive UI style guides that can be shared across different technology stacks.

Ready to Modernize Your React Applications?

Our team specializes in building scalable, framework-independent web applications using modern technologies like Shoelace and React.

Sources

  1. Shoelace React Framework Integration - Official documentation covering React component patterns, event handling, and TypeScript integration
  2. Shoelace Installation Documentation - Complete installation guide for CDN and npm methods
  3. LogRocket: Shoelace Build Web Components React - Tutorial covering Shoelace basics and React usage patterns
  4. The New Stack: Shoelace Web Components Library - Overview of Shoelace as framework-independent component library