---
url: 'https://www.corbado.com/blog/component-first-developer-tool-startup'
title: 'How to Build a JavaScript UI Component-First DevTool Startup in 2024'
description: 'Learn how to build a JavaScript UI component-first dev tool startup & understand why many move away from web components to framework-specific components.'
lang: 'en'
author: 'Vincent'
date: '2024-07-06T09:27:23.994Z'
lastModified: '2026-03-25T10:00:35.201Z'
keywords: 'component-first, web components ssr, web components hydration, web components server side rendering, client side rendering web components'
category: 'Engineering'
---

# How to Build a JavaScript UI Component-First DevTool Startup in 2024

## Key Facts

- Corbado shifted from **web components to framework-specific components**, citing SSR
  incompatibility, missing reactivity and lack of DOM diffing as key technical drivers.
- **Flash of Unstyled Content (FOUC)** emerges during hydration because web components
  rely on browser-only APIs like `customElements.define()` that are unavailable
  server-side.
- **Declarative Shadow DOM (DSD)** can bring SSR to custom elements but requires rewriting
  existing components and full standardization remains years away.
- Corbado's recommended **Vanilla JavaScript SDK** approach centralizes business logic in
  one package, then layers framework-specific UI components on top for broad SSR support.

## 1. Introduction: Component-First DevTool Startups

I’ve been working on Corbado, a component-first developer tool startup, to provide
developers with fast and easy-to-implement passkeys for user authentication. Our technical
approach is based on JavaScript UI components that we continually refine to meet the
evolving needs of developers and optimize the developer experience (DX).

Recently, we've made a significant **shift from using web components to framework-specific
components** (we call them UI components or web-js components). If I started all over
again, I would immediately build framework-specific components instead of using web
components.

In this blog post, I want to share our learnings while working with web components,
provide the reasons behind the transition away from web components and explain why many
other JavaScript UI component-first startups and developers follow this path.

This blog post should help everyone who is thinking of building a JavaScript UI
component-first, developer tool startup determine the right tech stack and benefit from
our learnings. Specifically, we try to answer the following questions:

1. **Why are all JavaScript UI component-first developer tool startups dropping web
   components?**
2. **What options to determine the component tech stack exist?**

We’ll start by defining certain use cases and scenarios before taking a deeper look at web
components (especially their drawbacks) until we showcase potential strategies to use
components. Let’s dive in!

## 2. When to Follow a Component-First Strategy?

Before going into the technical details, let’s have a look at different business use cases
for using a component-first strategy. In general, we assume that following a
component-first strategy (in contrast to an API-first strategy) is preferred if you want
to ship a frontend / UI layer of your devtool. This means that APIs are encapsulated in
your component, business logic is built-in and UX / UI flows are also part of your
product. Typical use cases for using component-first products can include:

- Onboarding & guides
- Promotion
- Surveys & feedback
- Authentication
- [Payment](https://www.corbado.com/passkeys-for-payment)
- Data visualization

Besides these use cases, we define three common company scenarios that we want to use in
our analysis. In [section 7](#7-recommendation-for-component-strategy), you’ll find
specific recommendations on which strategy to follow for these scenarios.

|                            | **Scenario 1**                                                                                                                                              | **Scenario 2**                                                                                                              | **Scenario 3**                                                                                                                                                                   |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name**                   | Regular Startup                                                                                                                                             | DevTool Startup                                                                                                             | Enterprise                                                                                                                                                                       |
| **Description**            | - Startup that builds a digital product <br/> - Wants to use an external solution for non-core features <br/> - Is the customer of a developer tool startup | - Startup that builds components <br/> - Components are sold to external customers                                          | - Larger company with many departments <br/> - Builds components for internal use across different departments <br/> - Internal colleagues are the “customers” of the components |
| **Main goals**             | - Save time & outsource stuff <br/> - Use something pre-built from experts <br/> - Focus on core features                                                   | - Provide great developer experience <br/> - Support many frameworks <br/> - Decrease complexity for maintaining components | - Re-use components <br/> - Have one maintainer and multiple applications <br/> - Use component in different frameworks in different dedicated teams                             |
| **Technical requirements** | - Use SSR for SEO purposes and get technology benefit <br/> - Use component in one JavaScript framework                                                     | - Offer component in all major JavaScript frameworks                                                                        | - SSR is nice-to-have <br/> - Use components in own design system                                                                                                                |

We know that we’re probably deep in the UI JavaScript component-first devtool startup
bubble and not all readers of this blog post have an idea what companies are actually
behind that. Therefore, we provide an exemplary list of companies that we assess as being
a JavaScript UI component-first developer tool company:

- **Frigade**: components for onboarding and promotion
- **Clerk**: components for B2B [SaaS](https://www.corbado.com/blog/saas-companies-integrate-passkeys)
  authentication
- **Corbado**: components for passkey authentication
- **Mollie**: components for [payment](https://www.corbado.com/passkeys-for-payment)
- **Refine**: components for internal tools & admin panels
- **SaaS UI**: components for dashboards

After we’ve presented the different use cases, scenarios and sample developer tool
companies in the component-first universe, let’s go back to the tech part. We start with
web components and answer the question why many move away from them. To better understand
the delusion about web components and their abundance, we need to understand their
characteristics first.

## 3. What are Web Components?

Web components are framework-agnostic components that are built on browser APIs. They can
bundle a lot of intelligence behind a couple of lines of code, so that other developers
can quickly reuse them. You can basically implement custom HTML tags and use them like
`<my-component name=”component1></my-component>`besides regular HTML tags like `<b>`.

A great collection of useful resources around web components can be found
[here](https://github.com/web-padawan/awesome-web-components) or
[here](https://open-wc.org/).

Let’s have a look at the inner blocks of web components and two core concepts: custom
elements and shadow DOM.

### 3.1 Custom Elements

**Custom Elements enable you to attach a JavaScript class to an HTML element** on your
page. This allows you to add behaviors and JavaScript-generated content to any element
instances, automatically initializing them for the full lifecycle of the page, whether
they are server-rendered, JavaScript-generated, or injected via `fetch()`.

However, custom elements cannot be void elements (like `<img>` or `<meta>`). They must
have both a start and an end tag. Additionally, custom elements must include a dash in
their tag name to avoid conflicts with future web platform additions.

### 3.2 Shadow DOM

The **shadow DOM is a web standard that encapsulates a section of the DOM and its
s**tyles, providing isolation from the main document's DOM and CSS to avoid conflicts. It
enables the creation of reusable, self-contained web components by ensuring that styles
and scripts do not interfere with the rest of the document.

It addresses the repetitive authoring of markup at the cost of client-side rendering. This
trade-off introduces complexities such as managing layout shifts and the **Flash Of
Unstyled Content (FOUC)**. Common solutions to these issues often involve putting all
JavaScript into the critical rendering path (in the `<head>`) or hiding components until
they are defined via JavaScript – both of which can impact performance for critical
content.

![web component shadow dom](https://www.corbado.com/website-assets/web_component_shadow_dom_7e1d613993.png)_Taken
from
[https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM)_

### 3.3 How to Build a Web Component

Building web components can be accomplished using various frameworks and libraries, each
offering unique benefits. Popular options include Vanilla JavaScript, Lit, Stencil, and
frameworks like [Angular](https://www.corbado.com/blog/angular-passkeys) and [Vue](https://www.corbado.com/blog/vuejs-passkeys). Here’s
a brief overview of how to create a basic web component using Vanilla JavaScript and Lit.

#### 3.3.1 How to Build a Web Component with Vanilla JavaScript

Vanilla JavaScript provides a straightforward approach to creating web components. Here’s
a simple example:

```javascript
class MyElement extends HTMLElement {
    constructor() {
        super();
        const shadow = this.attachShadow({ mode: "open" });
        shadow.innerHTML = `<p>Hello, Web Component!</p>`;
    }
}
customElements.define("my-element", MyElement);
```

The web component can then be used in the HTML like:

```html
<my-element></my-element>
```

In this example, we have defined a custom element `my-element` with a shadow DOM that
contains a simple paragraph (“Hello, Web Component!”).

#### 3.3.1 How to Build a Web Component with Lit

Lit is a library that simplifies the creation of web components with declarative templates
and reactive properties. Here’s how you can create a similar component using Lit:

```javascript
import { LitElement, html, css } from "lit";

class MyLitElement extends LitElement {
    static styles = css`
        p {
            color: blue;
        }
    `;

    render() {
        return html`<p>Hello, Web Component with Lit!</p>`;
    }
}
customElements.define("my-lit-element", MyLitElement);
```

In this Lit example, we define a custom element `my-lit-element` with scoped styles and a
template that renders a styled paragraph.

**Other Frameworks to Create Web Components:**

- **Stencil**: A compiler that generates standards-compliant web components, allowing you
  to use modern web APIs while maintaining compatibility with older browsers.
- **Angular**: Provides “[Angular](https://www.corbado.com/blog/angular-passkeys) Elements”, a feature that lets
  you package [Angular](https://www.corbado.com/blog/angular-passkeys) components as custom elements.
- **Vue**: Supports custom elements via the “[vue](https://www.corbado.com/blog/vuejs-passkeys)-custom-element”
  library, enabling you to create reusable [Vue](https://www.corbado.com/blog/vuejs-passkeys) components as web
  components.
- **Lit**: Provides a minimalistic approach with a small API surface and leverages
  JavaScript's modern capabilities to create highly efficient components with reactive
  properties and template-based rendering.
- **Enhance:** Offers a straightforward API for building encapsulated and reusable
  components, emphasizing developer productivity and component modularity.
- **WebC**: Aims to provide a seamless development experience by integrating well with
  existing workflows and offering utilities that make building and managing web components
  more efficient.

Using these frameworks and libraries, you can build powerful, reusable web components.
Each approach offers different levels of abstraction and features, allowing you to choose
the best tool for your project's specific needs.

At this point, we don’t want to go into more detail about building web components, as
there’s already a plethora of good content available. To read more about web components in
general, I recommend having a look at
[Zach Leatherman’s blog](https://www.zachleat.com/web/good-bad-web-components/). Also,
this
[list of some prominent web component proponents](https://arewebcomponentsathingyet.com/)
is a nice overview to see who’s using web components.

## 4. What are the Benefits of Web Components?

Often hailed as the holy grail for web development, web components have been described as
a potential
[replacement for all JavaScript frameworks](https://gomakethings.com/will-web-components-replace-react-and-vue/)
(I would be interested in what the [React](https://www.corbado.com/blog/react-passkeys),
[Vue.js](https://www.corbado.com/blog/vuejs-passkeys) or [Next.js](https://www.corbado.com/blog/nextjs-passkeys) maintainers actually
think of those statements).

Let's explore the key advantages that make web components so appealing to some.

### 4.1 Usable in Most JavaScript Frameworks

One of the most significant benefits of web components is their framework-agnostic nature.
They can seamlessly integrate with various JavaScript frameworks, making them highly
versatile. Initially described as "a browser-native alternative to
[React](https://www.corbado.com/blog/react-passkeys)," web components have gained widespread support across the
modern web ecosystem. For a detailed overview of their compatibility with different
frameworks, you can [check out this website](https://custom-elements-everywhere.com/).
It's worth noting that [React](https://www.corbado.com/blog/react-passkeys) scores poorly on this compatibility
test (for a more future-friendly and compatible experience, consider using Preact).

![web component support frameworks](https://www.corbado.com/website-assets/web_component_support_frameworks_e1316a424b.png)

### 4.2 Wide Browser Support

Web components enjoy wide browser support, making them a reliable choice for cross-browser
compatibility. Modern browsers, including Chrome, Firefox, Safari, and Edge, all support
web components (basically since the end of life of Internet Explorer in June 2022, they
have gained universal browser support). This broad compatibility simplifies development
and reduces the need for polyfills or workarounds.

![custom elements support browser](https://www.corbado.com/website-assets/custom_elements_support_browser_5075ccdf5d.png)

### 4.3 Progressively Enhance Existing HTML

Where web components truly shine is in their ability to progressively enhance existing
HTML. They serve as an excellent mechanism for adding interactivity to your HTML without
requiring a complete rewrite. Here are some practical examples of how web components can
enhance your forms and content:

```html
<ajax-form>
    <form action="/path/to/server" method="post">
        <label for="username">Username</label>
        <input type="text" name="username" id="username" />

        <label for="password">Password</label>
        <show-password>
            <input type="password" name="password" id="password" />
        </show-password>

        <button>Login</button>
    </form>
</ajax-form>
```

In this example, we use custom elements to add AJAX functionality to a form (custom
element called `<ajax-form>`) and a show/hide toggle to a password field (custom element
called `<show-password>`). Web components allow you to generate and inject HTML, respond
to user interactions, and encapsulate functionality in a convenient wrapper.

With web components, there's no need to instantiate libraries or pass selectors for target
elements. They handle multiple elements and their behavior effortlessly, providing a clean
and efficient way to enhance your web pages. This convenience makes web components an
attractive choice for developers looking to streamline their workflow and deliver rich,
interactive experiences.

## 5. Why are Component-First Startups Dropping Web Components?

Despite all the advantages that web components promise, many developer-first startups,
that focus on building JavaScript UI components, ditch web components in favor of
framework-specific components.

Let’s try to understand why (for more detailed insights about the issues of web components
we also recommend
[Carlana Johnson’s](https://blog.carlana.net/post/2023/web-component-alternative-futures/)
and [Dave Rupert’s blog](https://daverupert.com/2023/07/why-not-webcomponents/)).

### 5.1 Lack of Framework-Specific Features

While web components offer substantial flexibility, they lack many of the
framework-specific features that developers have come to rely on.

Here’s a list of some framework-specific characteristics and features that web components
lack:

- **Web component integration is not seamless**\
  Depending on the framework that you’re using the integration of web components, even
  though only requiring a few lines of code, isn't always as seamless as with
  framework-specific components, leading to a less optimal developer experience.
- **Frameworks already solve problems that web components try to solve**\
  Web components were initially designed as low-level primitives for framework authors.
  However, many framework authors did not adopt them widely because their existing
  problems were already solved within their frameworks. Switching to web components and
  shadow DOM, despite potential performance benefits, would disrupt established
  conventions in these frameworks.
- **Web components cannot be easily styled within frameworks**\
  Although support for importing and exporting web components has improved in many
  frameworks, there are still challenges, such as style encapsulation, that hinder
  widespread adoption.
- **Web components have inferior reactivity in response to JavaScript changes**\
  Frameworks like React and Vue have great features in response to JavaScript changes
  (e.g. automatic UI updates). This reactivity means developers don’t have to manually
  manage updates. Instead, the framework handles it. Web components, on the other hand,
  lack a built-in mechanism for this type of reactivity. While you can achieve similar
  behavior using JavaScript proxies, developers typically prefer tools that handle this
  automatically.
- **Web components cannot diff DOMs and cannot render selectively**\
  Even though web components offer reactivity for HTML attributes, they lack a
  browser-native way to diff the DOM and selectively render only the elements that have
  changed. This capability is a key feature in frameworks like React and Vue, which
  enhances performance and simplifies the development process.
- **Web components are not built for single-file components**\
  Many developers appreciate the single-file component approach used in frameworks like
  React and Vue, where HTML, CSS, and JavaScript are all contained within a single file.
  Although web components can approximate this approach, it’s not their primary design.
  Web components adhere to the web's fundamental principle of separating concerns, which
  can be a drawback for developers accustomed to single-file components.

In summary, while web components work with the fabric of the web and offer significant
flexibility, their lack of framework-specific features and less seamless integration can
result in a less favorable developer experience. For those deeply embedded in framework
ecosystems, the transition to web components might involve significant trade-offs.

### 5.2 Web Components have a Marketing Problem

The marketing journey of web components has been rocky according to
[Dave Rupert](https://daverupert.com/2023/07/why-not-webcomponents/), primarily due to
pushy advocacy and early confusion between web components (the specifications) and Polymer
(the Google UI framework). Polymer's early years were fraught with issues: it was
positioned as a competitor to other UI frameworks, including Google's own Angular and
faced setbacks like Mozilla's removal of HTML imports. These factors contributed to a
shaky foundation that hindered widespread adoption.

Despite these real adoption concerns, some Google advocates took to
[Twitter](https://www.corbado.com/blog/x-twitter-passkeys) with an aggressive stance, dismissing React as
inferior and slow while suggesting that anyone using it was making a poor choice. This
confrontational strategy did not help in winning over developers. Simultaneously, Google's
AMP project, which also utilized web components, pushed a similar narrative, presenting
itself as a necessary remedy for the bloated mobile web. This approach further alienated
many in the web development community.

However, much has changed over the years. The introduction of Lit has addressed many of
the issues and confusion that plagued Polymer, and web components are no longer seen as
just "a Google thing." Today, there are numerous non-Google web
[component libraries](https://www.corbado.com/blog/react-component-libraries) that are gaining traction. Still,
there are many misconceptions around web components.

### 5.3 Web Components and Server-Side Rendering (SSR) is a Pain

One of the most significant challenges with web components is their lack of support for
Server-Side Rendering (SSR) for quite a long time. SSR can greatly enhance the performance
of web applications, particularly for initial page loads, by rendering HTML on the server
before sending it to the client. This approach is especially advantageous for SEO and
improves the UX, particularly for those on slower connections. However, achieving SSR with
web components is notoriously difficult.

Web components rely heavily on browser-specific DOM APIs, such as
`customElements.define()`, `HTMLElement` or `template.content.cloneNode()`, which are
unavailable on the server. This presents a major hurdle when trying to integrate web
components with SSR frameworks like [Next.js](https://www.corbado.com/blog/nextjs-passkeys).

Many developers integrate web components in server-side code and expect that things are
working out-of-the-box. However, this is not the case (as we see below). Moreover, many
JavaScript frameworks offer the possibility to define certain components or pages to be
client-side-rendered while the rest of the website is server-side rendered. However, often
this causes a lot of confusion and is for unexperienced developers complex to implement.

To grasp the full extent of web components and SSR, it's essential to understand the
typical SSR workflow (see example for React and [Next.js](https://www.corbado.com/blog/nextjs-passkeys) below)
and how this workflow diverges when web components are involved.

#### 5.3.1 How React Code Works in Next.js to Support SSR

React is a client-side rendered framework. Next.js, which builds upon React, supports SSR.
If you are using SSR components in a React/Next.js application, the flow looks as follows:

1. **Initial Server Request**:\
   When a user navigates to a web page, a request is sent to the server.
2. **Server-Side Rendering**:\
   In an application with Next.js, the React code is transformed into HTML on the server
   side. The server uses [React's](https://www.corbado.com/blog/react-passkeys) rendering engine to generate a
   string of HTML that represents the initial state of the application, e.g.:
   `ReactDOMServer.renderToString(<App/>)` generates a string of HTML for the entire React
   component tree.
3. **Send HTML Response to the Browser**:\
   This HTML is then sent to the user's browser along with JavaScript files containing the
   React components and their logic. The server sends the fully rendered HTML back to the
   client, which is immediately usable by the browser, allowing users to see content
   quickly.
4. **Hydration**:\
   Upon receiving this HTML and JavaScript, the browser will re-render the React component
   tree, matching it with the server-rendered HTML. This process, known as **hydration**,
   makes the web app interactive by wiring up event handlers and initializing state, e.g.:
   `ReactDOM.hydrate(<App/>, document.getElementById('root'))` connects the React
   components to the server-rendered HTML, effectively "hydrating" the static content to
   become fully interactive.

This all results in the following benefits:

- **Improved Performance**: By sending pre-rendered HTML from the server, the initial page
  load is faster, improving the user experience, especially for those on slower
  connections.
- **SEO Advantages**: Search engines can index the pre-rendered HTML more effectively,
  enhancing the discoverability of the content.
- **Reduced Time to Interactive (TTI)**: Users can see and interact with the content more
  quickly as the heavy lifting of rendering is done on the server.

#### 5.3.2 The Hydration Challenge with Web Components

You have seen the process for SSR with framework-specific components above. Web components
have not been supporting SSR natively for a long time and caused the following challenge:

During hydration, JavaScript code that registers web components is executed. Typically,
this code is included in the application’s JavaScript bundle via imports. As a result,
there is a period between the initial render of the SSR HTML and the completion of the
hydration process where web components might not display the correct content. This leads
to a **flash of unstyled content (FOUC)**, where the web page initially appears unstyled
or incorrectly styled until the JavaScript fully loads and executes.

This delay can cause layout shifts and visual inconsistencies, negatively impacting user
experience. While theoretically, you could try to match the final rendered output with
placeholder markup, this is highly impractical, especially for complex or third-party web
components.

Nevertheless, recently, there are some developments on the way that try to fix SSR support
for web components.

### 5.4 How to Fix SSR Support for Web Components

One motion to fix SSR support for web components is through the efforts of the different
underlying frameworks, each attempting to add SSR support in various ways. However, many
of these initiatives are not yet stable. For instance,
[Lit’s SSR capabilities](https://lit.dev/docs/ssr/overview/) are still part of
"[Lit Labs](https://lit.dev/docs/ssr/overview/)," highlighting the experimental nature of
these solutions.

A robust, standardized solution for SSR in web components will likely require the ability
to render all web components on the server allowing them to compose and hydrate
seamlessly, independently of the framework used to create them. This goal is still a few
years away, indicating that significant advancements are needed before SSR for web
components can be considered fully resolved.

One promising approach to achieving this goal is
[Declarative Shadow DOM (DSD)](https://developer.chrome.com/docs/css-ui/declarative-shadow-dom)
which allows for SSR across frameworks. However, DSD presents several challenges and many
existing web components have to be rewritten to use DSD and support SSR.

To better understand the potential and challenges of **Declarative Shadow DOM**, let’s
explore it in more detail.

#### 5.4.1 What is Declarative Shadow DOM?

[Declarative Shadow DOM](https://developer.chrome.com/docs/css-ui/declarative-shadow-dom)
is a web specification that enables server-side rendering (SSR) for custom elements by
allowing developers to define shadow roots directly in HTML. This approach simplifies the
use of shadow DOM in web components.

Traditionally, creating custom elements required defining a class and constructing a
shadow root within the constructor which resulted in the shadow DOM. Before Declarative
Shadow DOM, a major SSR challenge was the need for client-side JavaScript to attach the
shadow DOM, which couldn’t be executed server-side. Servers would return a pre-rendered
version of the web component, complete with classes and layouts for the client-side
JavaScript to replace with the actual shadow DOM during hydration. For example, Stencil
uses this method to support SSR for web components.

The general support for DSD is already pretty high
[among browsers](https://caniuse.com/declarative-shadow-dom).

![declarative shadow dom support browser](https://www.corbado.com/website-assets/declarative_shadow_dom_support_browser_6db02ef26a.png)

Let’s better understand the difference between imperative (the old and existing way) and
declarative shadow DOM.

#### 5.4.2 Imperative Shadow DOM

With imperative shadow DOM, for instance, a custom element named `AppCard` would be
imperatively constructed as follows:

```javascript
class AppCard extends HTMLElement {
    constructor() {
        super();
        const shadowRoot = this.attachShadow({ mode: "open" });
        shadowRoot.innerHTML = `
      <style>
        /* Styles scoped to this element */
      </style>
      <div>
        <!-- Template content -->
      </div>
    `;
    }
}

customElements.define("app-card", AppCard);
```

In this example, the `AppCard` custom element creates its shadow root in the constructor,
providing encapsulation for its styles and structure.

Interactivity within the web component is still given even though the template is reduced
to a string. The web component would be imperatively constructed for the client-side, but
since the shadow root is already instantiated, you don’t need to instantiate the template.

#### 5.4.3 Declarative Shadow DOM

With declarative shadow DOM, you can define the same template for the custom element in a
more declarative manner. Here is the same shadow root definition using declarative shadow
DOM:

```html
<app-card>
    <template shadowrootmode="open">
        <style>
            /* Styles scoped to this element */
        </style>
        <div>
            <!-- Template content -->
        </div>
        <slot></slot>
    </template>
    <!-- Light DOM content that will be projected through the slot -->
</app-card>

<script>
    class AppCard extends HTMLElement {
        constructor() {
            super();
            // No need to attach shadow root here, it's done declaratively in HTML
        }
    }
    customElements.define("app-card", AppCard);
</script>
```

In this DSD approach, the `shadowroot` attribute in the HTML template is recognized by the
HTML parser and applied as the shadow root of its parent element (`<app-card>`). The
template slots allow dynamic content injection into the custom element template. Any HTML
outside of the template is considered "Light DOM" and can be projected into the "Shadow
DOM" via slots.

Let’s briefly summarize the advantages and disadvantages of DSD.

#### 5.4.4 Advantages of Declarative Shadow DOM

1. **Bring SSR Support to Web Components**: By defining templates declaratively, SSR
   becomes feasible for custom elements. The browser can parse and apply the shadow root
   without needing to execute JavaScript, ensuring the initial load is styled and
   structured correctly.
2. **Reduced JavaScript Complexity**: Since the shadow root is already instantiated by the
   browser, you don’t need to create it imperatively in the constructor, simplifying your
   client-side code.
3. **Template Reusability**: ES2015 template strings enable you to define templates once
   and reuse them both declaratively (in the HTML) and imperatively (in the JavaScript).
   This approach reduces redundancy and maintains consistency across your component
   definitions.

#### 5.4.5 Disadvantages of Declarative Shadow DOM

1. **SEO Challenges:** While server-side rendering helps with initial load performance, it
   can create challenges for search engine optimization. Shadow DOM content may not be
   indexed effectively by search engines, potentially impacting visibility and
   discoverability.
2. **Complexity in State Management:** Declarative Shadow DOM separates template
   definition from JavaScript logic. This can make it harder to manage dynamic state
   changes and interactions within the shadow root, requiring more intricate patterns to
   ensure state consistency.
3. **Debugging Difficulties:** Debugging issues within the shadow DOM can be more
   challenging compared to traditional DOM. Tools and browser support for inspecting
   shadow roots and their content are still evolving, potentially leading to increased
   debugging time.
4. **Performance Overheads:** Although Declarative Shadow DOM can enhance initial load
   performance, the overall rendering process might introduce additional performance
   overhead. Parsing and applying complex shadow roots declaratively may affect runtime
   performance in certain scenarios.

## 6. Overview of Component-First Strategies

Apparently, web components have some drawbacks (as described above). That’s why we tried
to conduct more holistic research and find alternative ways to follow a component-first
strategy. While doing so, we found different approaches which can be summarized into the
following categories.

### 6.1 Web Components

The first category are classical web components that we have already described in detail
above. The classical web components would be written in one framework, e.g.
[Vue.js](https://www.corbado.com/blog/vuejs-passkeys) or Lit, and could be used in most other JavaScript
frameworks.

**Pros:**

- One code base for all frameworks

**Cons:**

- No SSR support for all web components and underlying frameworks
- No support of framework-specific features

### 6.2 React-Based Components

The second category of components use React as foundation (as React is the most popular
JavaScript framework according to many surveys and usage data). This means you would write
React components (so no web components) and use them as follow:

- **For React-based frameworks (e.g. React, Next.js, Remix)**: use the React component
  directly. This allows for SSR support and the support of other React-specific features.
- **For all other frameworks (e.g. Vue.js, Angular)**: render the entire React component
  into a `<div>`, which will be used as the component. For these other frameworks, SSR
  would not be supported.

**Pros:**

- React is supported natively (e.g. SSR support for React-based frameworks is given)
- Simple to maintain as only one framework / component needs to be maintained

**Cons:**

- No SSR support for non-React-based frameworks
- No support for framework-specific features of non-React-based frameworks

### 6.3 React Component and Web Component in Parallel

The third category was a mixture of both worlds. Here, you would build a React component
and besides a separate web component for all other frameworks (here are free to decide for
an underlying framework e.g. [Vue.js](https://www.corbado.com/blog/vuejs-passkeys) or Lit).

For React, SSR would be supported. For all other frameworks, that are using the web
component, SSR and other framework-specific features would not be supported necessarily.

**Pros:**

- React is supported natively (e.g. SSR support for React)

**Cons:**

- Two code bases need to be maintained
- No support for framework-specific features of non-React-based frameworks

### 6.4 Vanilla JavaScript SDK for Multiple Frameworks

The fourth category is the one that we decided on. To understand why we came up with this,
you need to understand where we came from.

Initially, we decided to go for the web component category, as we thought that there must
be a smooth way to get SSR working for our existing web that we were already using
(written in Vue.js). So, we thought there shouldn’t be too much of rework to be done (just
some minor adjustments should do the job).

This approach would allow us to stay framework-agnostic and we had the following options:

- **Option 1: Keep on using Vue.js as basis for our web components**\
  This implies that SSR would need to be implemented for Vue.js, removing us from the
  React-based space, where many other great developer tools are currently built around the
  React / Next.js / [Vercel](https://www.corbado.com/blog/vercel-passkeys) ecosystem. To implement SSR for Vue.js
  based web components,
  [some documentation is available](https://vuejs.org/guide/extras/web-components.html).
- **Option 2: Rewrite web components with React**\
  If we re-wrote the current web components with React to stay in the React / Next.js /
  [Vercel](https://www.corbado.com/blog/vercel-passkeys) ecosystem, we would still need to build dedicated React
  components to enable SSR.
- **Option 3: Rewrite web components with Lit**\
  Lit has only a lab version that addresses SSR (see above). However, the Lit approach
  would require some substantial re-writing of our components and we didn’t consider Lit
  as mature as other frameworks.
- **Option 4: Parallel development of React and web components**\
  This approach would require creating React components and work in parallel with web
  components, maintaining two parallel implementations. While you could get the best of
  both worlds, it would mean a lot of maintenance work to keep everything up-to-date and
  in sync.
- **Option 5: Create new React components and render them into `<div>`**\
  This approach requires the creation of entirely new React components, which would allow
  for SSR in React-based frameworks. To support other frameworks, the content of the React
  based components would be rendered into a `<div>` (however SSR would still only work
  then for React-based frameworks).

While evaluating the approaches for the new component strategy, it became evident that all
the five options were not sufficient. So, we shifted our strategy towards a new approach.

#### 6.4.1 The New Overall Architecture

The strategy we ended up with is an extension of option 5.

**General**

Our components are usable for both JavaScript and TypeScript developers. We employ a mono
repository approach, consolidating all frontend TypeScript code into a single, open-source
GitHub repository. This repository houses multiple packages, each of which can be
independently published on npm.

![corbado component strategy package dependencies](https://www.corbado.com/website-assets/corbado_component_strategy_package_dependencies_829815c92b.png)

We leverage OpenAPI code generation to streamline the creation of REST clients and types
for both our Frontend API and Backend API. Our testing framework primarily utilizes
[Playwright](https://www.corbado.com/blog/passkeys-e2e-playwright-testing-webauthn-virtual-authenticator),
complemented by unit tests in Jest and Mocha. For bundling, we use webpack, with a future
consideration to transition to turbopack as it matures. Additionally, our setup ensures
compatibility with IE11 and supports both ES5 and ES6 standards.

**@corbado/web-core**

Our component architecture is built around a Vanilla JavaScript SDK
([@corbado/web-core](https://www.npmjs.com/package/@corbado/web-core)) that encapsulates
business logic and data. This Vanilla JavaScript SDK does not include any UI components,
allowing it to serve as a centralized repository for our business logic, thus eliminating
the need to maintain this logic in multiple places. Its versatility allows it to be used
not only within pre-built framework-specific components by Corbado, but also to create
custom logic, build other framework-specific components, or apply unique UIs on top of it.
@Corbado/web-core is an internal package and does not need to be installed separately.

**@corbado/react**

As the majority of our customers are from the React / Next.js /
[Vercel](https://www.corbado.com/blog/vercel-passkeys) ecosystem, we decided to focus on React and build a
dedicated React component
([@corbado/react](https://www.npmjs.com/package/@corbado/react)). This React component
consumes the Vanilla JavaScript SDK, handles the UI and leverages React-specific features,
such as session management through providers.

**@corbado/react-sdk**

To allow for more custom implementations, we also built a React SDK
([@corbado/react-sdk](https://www.npmjs.com/package/@corbado/react-sdk)) which is tailored
for React applications that require more control over authentication flows without
prebuilt UI components.

**@corbado/web-js**

To support other frameworks as well, the React component (@corbado/react) is rendered into
a `<div>`, which can then be integrated in other JavaScript frameworks that we do not yet
support with dedicated components (yet). These components are called web-js components
([@corbado/web-js](https://www.npmjs.com/package/@corbado/web-js)).

**@corbado/types**

To provide TypeScript support, this package
([@corbado/types](https://www.npmjs.com/package/@corbado/types)) holds the TypeScript type
declarations for all Corbado packages and is automatically included when installing the
other packages.

**@corbado/shared-ui**

This package ([@corbado/shared-ui](https://www.npmjs.com/package/@corbado/shared-ui))
contains shared files for all UI components (so currently for @corbado/react and
@corbado/web-js). It’s automatically installed when any of these packages is installed.

#### 6.4.2. Advantages of Corbado’s Component Strategy

We spent much time on coming up with this component strategy and decided to take this
route based on the following advantages:

- **Easily Add Dedicated Components for More Frameworks:** Our modular architecture allows
  us to seamlessly add support for additional framework-specific components, such as
  Angular and Vue.js. This approach leverages framework-specific features while keeping
  maintenance efforts minimal.
- **Support for All JavaScript Frameworks Out-of-the-Box:** With the web-js / UI
  component, we can support all JavaScript-based frameworks with one component that holds
  all the business logic, data and UI out-of-the-box.
- **Best Support for React / Next.js / Vercel Ecosystem**: Our core logic is currently
  written in React, which aligns with the greatest adoption of our solution among React /
  Next.js developers. Given the high demand from developers for React support, we are able
  to provide the best possible support for the React / Next.js / Vercel ecosystem (also
  here
  [considerable work regarding SSR support has been done](https://vercel.com/blog/understanding-react-server-components)).
- **Centralized Logic in One Repository:** All our business logic for providing a great
  authentication experience is centralized in one repository. This centralization
  simplifies future modifications and updates, ensuring consistency and reliability.
- **Flexibility for Developers to Build Custom UI and Logic:** Our Vanilla JavaScript SDK
  enables developers to create native authentication experiences across any
  JavaScript-capable platform, including web, backend, and mobile environments. This
  flexibility allows for tailored solutions that meet diverse needs.
- **Support for SSR Across All Frameworks:** By developing framework-specific components,
  we can leverage SSR capabilities inherent to each framework, enhancing performance and
  user experience across various platforms.

#### 6.4.3 Disadvantages of Corbado's Component Strategy

- **Multiple Codebases for Framework-Specific Features:** To fully utilize
  framework-specific features, we need to develop separate components / packages for each
  framework. Although the core business logic remains reusable via the Vanilla JavaScript
  SDK, this approach requires additional effort for framework-specific implementations.
- **Framework-Specific Components for SSR Support:** Similar to framework-specific
  features, implementing SSR support necessitates creating distinct components tailored to
  each framework. This requirement adds to the complexity and development workload.

#### 6.4.4 Our Current State of Transition from Web components to UI Components

So far, we have fully replaced web components with the new component structure described
above. This means that there is currently a React component and a web-js / UI component
for all other frameworks. Other framework-specific components will follow soon. In one of
our next major releases, we will add SSR capabilities to our React component and therefore
provide a dedicated @corbado/nextjs package. Then, also @corbado/vuejs, @corbado/angular,
@corbado/[svelte](https://www.corbado.com/blog/svelte-passkeys), @corbado/nuxtjs are planned.

## 7. Recommendation For Component Strategy

As we want to help other developers and developer tool companies in making use our
learnings and research, we try to provide specific recommendations on which component
strategy to follow based on different scenarios.

|                                          | **Scenario 1**                                                                                                                                                    | **Scenario 2**                                                                                                                                                                                        | **Scenario 3**                                                     |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| **Name**                                 | Regular Startup                                                                                                                                                   | DevTool Startup                                                                                                                                                                                       | Enterprise                                                         |
| **Recommended Component-First Strategy** | - Use framework-specific components in the framework that you’re already using <br/> - Don’t try to reinvent the wheel by coming up with own component-strategies | - Build a Vanilla JavaScript SDK that holds the business logic and data <br/> - Use this base repository to build many framework-specific components without the need to maintain too many code bases | - Use web components (if support of SSR is not a hard requirement) |
| **Benefit**                              | Best performance and DX in a framework that you already know                                                                                                      | Wide support of frameworks; SSR for the most important frameworks while having as little code bases as possible                                                                                       | Only one code base to maintain                                     |

## 8. Conclusion

Building a JavaScript UI component-first developer tool startup requires careful
consideration of the right tech stack. From our experience with Corbado, transitioning
from web components to framework-specific components has proven beneficial.

Framework-specific components offer better integration, performance, and developer
experience, especially with critical features like SSR. By centralizing business logic in
a Vanilla JavaScript SDK and leveraging framework-specific UI components, you can achieve
broad compatibility and maintainable code. This approach ensures flexibility, performance,
and the ability to quickly adapt to evolving developer needs.

## Frequently Asked Questions

### What is the recommended component strategy for a DevTool startup that needs to support multiple JavaScript frameworks?

Build a Vanilla JavaScript SDK that centralizes all business logic and data, then layer
framework-specific UI components on top. Start with React given its broad ecosystem
adoption and render React components into a `<div>` to support other frameworks until
dedicated components are built. This approach minimizes the number of codebases while
enabling SSR for the most critical frameworks.

### Why does React have poor compatibility with web components?

React scores poorly on the custom-elements-everywhere.com compatibility test, making web
component integration in React applications less seamless than in other frameworks. For a
more compatible and future-friendly experience when tight web component integration is
required, using Preact is the recommended alternative.

### Should an enterprise use web components or framework-specific components for an internal design system?

Enterprises without a hard SSR requirement are advised to use web components, since they
offer a single codebase that works across all JavaScript frameworks used by different
internal teams. When SSR is not mandatory, the maintenance simplicity of one web component
codebase outweighs the absence of framework-specific features.

### What caused web component adoption to stall despite strong browser support?

Early confusion between the web components specification and Google's Polymer framework
damaged adoption, as Polymer was positioned as a competitor to frameworks like Angular and
faced setbacks such as Mozilla removing HTML imports. Aggressive social media advocacy
dismissing React and Google's AMP project pushing similar narratives further alienated
developers, creating misconceptions that persist today despite improvements introduced by
the Lit library.
