component first developer tool startupEngineering

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

Learn how to build a JavaScript UI component-first dev tool startup & understand why many move away from web components to framework-specific components.

Blog-Post-Author

Vincent

Created: July 6, 2024

Updated: September 10, 2024


Our mission is to make the Internet a safer place, and the new login standard passkeys provides a superior solution to achieve that. That's why we want to help you understand passkeys and its characteristics better.

Overview#

  1. Introduction: Component-First DevTool Startups

  2. When to Follow a Component-First Strategy?

  3. What are Web Components?

    3.1 Custom Elements

    3.2 Shadow DOM

    3.3 How to Build a Web Component

      3.3.1 How to Build a Web Component with Vanilla JavaScript

      3.3.1 How to Build a Web Component with Lit

  1. What are the Benefits of Web Components?

    4.1 Usable in Most JavaScript Frameworks

    4.2 Wide Browser Support

    4.3 Progressively Enhance Existing HTML

  1. Why are Component-First Startups Dropping Web Components?

    5.1 Lack of Framework-Specific Features

    5.2 Web Components have a Marketing Problem

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

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

      5.3.2 The Hydration Challenge with Web Components

    5.4 How to Fix SSR Support for Web Components

      5.4.1 What is Declarative Shadow DOM?

      5.4.2 Imperative Shadow DOM

      5.4.3 Declarative Shadow DOM

      5.4.4 Advantages of Declarative Shadow DOM

      5.4.5 Disadvantages of Declarative Shadow DOM

  1. Overview of Component-First Strategies

    6.1 Web Components

    6.2 React-Based Components

    6.3 React Component and Web Component in Parallel

    6.4 Vanilla JavaScript SDK for Multiple Frameworks

      6.4.1 The New Overall Architecture

      6.4.2 Advantages of Corbado's Component Strategy

      6.4.3 Disadvantages of Corbado's Component Strategy

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

  1. Recommendation for Component Strategy

  2. Conclusion

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
  • Data visualization

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

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

Substack Icon

Subscribe to our Passkeys Substack for the latest news, insights and strategies.

Subscribe

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 or here.

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 styles, 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 domTaken from 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 and Vue. 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:

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:

<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:

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 Elements”, a feature that lets you package Angular components as custom elements.
  • Vue: Supports custom elements via the “vue-custom-element” library, enabling you to create reusable Vue 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. Also, this list of some prominent web component proponents is a nice overview to see who’s using web components.

Slack Icon

Become part of our Passkeys Community for updates and support.

Join

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 (I would be interested in what the React, Vue.js or Next.js 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," 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. It's worth noting that React scores poorly on this compatibility test (for a more future-friendly and compatible experience, consider using Preact).

web component support frameworks

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

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:

<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 and Dave Rupert’s blog).

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, 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 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 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.

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 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 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 are still part of "Lit Labs," 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) 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 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.

declarative shadow dom support browser

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:

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:

<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 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 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 ecosystem. To implement SSR for Vue.js based web components, some documentation is available.
  • 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 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

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, 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) 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 ecosystem, we decided to focus on React and build a dedicated React component (@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) 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).

@corbado/types

To provide TypeScript support, this 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) 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).
  • 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, @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 1Scenario 2Scenario 3
NameRegular StartupDevTool StartupEnterprise
Recommended Component-First Strategy- Use framework-specific components in the framework that you’re already using
- 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
- 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)
BenefitBest performance and DX in a framework that you already knowWide support of frameworks; SSR for the most important frameworks while having as little code bases as possibleOnly 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.

Share this article


LinkedInTwitterFacebook

Enjoyed this read?

🤝 Join our Passkeys Community

Share passkeys implementation tips and get support to free the world from passwords.

🚀 Subscribe to Substack

Get the latest news, strategies, and insights about passkeys sent straight to your inbox.


We provide UI components, SDKs and guides to help you add passkeys to your app in <1 hour

Start for free