Andrew Evans Husband, engineer, FOSS contributor, and developer at AWS. Follow me at and

Building a SolidJS UI with Kobalte

5 min read 1603 104

Solid JS Kobalte Accessible Applications

Accessibility is an important part of web development, enabling individuals with disabilities to have access to the content and web apps that are necessary for day-to-day tasks. In particular, making applications accessible using ARIA standards helps individuals with disabilities to leverage things like screen readers.

In this article, we’ll walk through how to build a Solid UI with Kobalte, a toolkit for building accessible apps with Solid. To follow along with the code examples, feel free to clone my sample repo on GitHub. Let’s get started!

Jump ahead:

What is SolidJS?

Solid is a library that helps you build reactive and performant frontend applications. Solid utilizes a concept called signals, which help to preserve reactivity in the application:

import {createSignal} from "solid-js"

const [value, setValue] = createSignal("value");

Signals in Solid are very similar to the useState Hook that we see in React. Solid also tracks data side effects using createEffect, which is similar to the useEffect Hook in React:

// originally copied from
const [a, setA] = createSignal(initialValue);

// effect that depends on signal `a`
createEffect(() => doSideEffect(a()));

Solid supports stores, which centralize state management in your application:

// originally copied from
import { createStore } from "solid-js/store";
import type { StoreNode, Store, SetStoreFunction } from "solid-js/store";

function createStore<T extends StoreNode>(
  state: T | Store<T>
): [get: Store<T>, set: SetStoreFunction<T>];
type Store<T> = T;  // conceptually readonly, but not typed as such

Putting it all together, you can build components with Solid functions in a very similar way to how you would in a React application:

import { createSignal, onCleanup } from "solid-js";

function Counter() {
  const [count, setCount] = createSignal(0);
  const timer = setInterval(() => setCount(count() + 1), 1000);
    onCleanup(() => clearInterval(timer));

  return <div>Count: {count()}</div>;

Getting started with Kobalte

To add Kobalte to a Solid project, you can use the following simple npm install:

npm install @kobalte/core

Kobalte provides a set of components that follow the WAI-ARIA Authoring Practices, so the components you build with Kobalte are accessible by default. Kobalte components are composable and give you access to each component’s granular parts. The code below shows a TextField:

import { TextField } from "@kobalte/core";
import "./style.css";
function App() {
  return (
    <TextField.Root class="text-field">
      <TextField.Label class="text-field__label">Favorite fruit</TextField.Label>
      <TextField.Input class="text-field__input" />

Here, the TextField.Root is the basis for the component, but you also have TextField.Label and TextField.Input, which you can use to access properties and styles.

Kobalte comes without styling, so you can either directly apply Vanilla CSS or use a supported framework like Tailwind CSS. To learn more, check out the documentation on the Kobalte website. With Kobalte, there is a large number of components that you can choose from. In the next few sections, I’ll explain what they look like and show how you can either change your existing components or start fresh with Kobalte.

Your first Kobalte component

If you cloned my sample repo, then you’ll see a very simple Solid application:

Sample Kobalte Application Structure

Notice there are three pages in the application. WithoutKobalte.tsx is a page that has a basic to-do application with just an input field and values. WithKobalte.tsx is a page that has the same application as WithoutKobalte.tsx but with Kobalte components. Finally, KobalteAdvanced.tsx is a page that showcases more Kobalte components with styles.

For our first Kobalte component, let’s look at the input and button elements in the WithoutKobalte.tsx page:

    placeholder="What do you have to do today?"
    onClick={() => {
    Add Todo

The input and button elements are pretty simple and consistent with what you’d expect in a normal web application. We have a placeholder, a value defined for the input, and an onClick event set for the button. If you look at the same components in WithKobalte.tsx, you’ll see that we now have access to the granular pieces of these elements:

        input() !== 'something' ? 'invalid' : 'valid'
    <TextField.Label />
        placeholder="What do you have to do today?"
        You can have a description appear like this
    <TextField.ErrorMessage class={styles.error}>
        If you type anything other than "something" the error
    onClick={() => {
    Add Todo

You’ll notice that the TextField in particular has elements for Description, ErrorMessage, Label, and Input. Additionally, when you run the application, you’ll notice the presence of the ARIA attributes on the Kobalte components versus the standard web components.

The image below shows the WithoutKobalte.tsx page:

Without Kobalte TSX Page Example Image

While the image below shows the WithKobalte.tsx page:

Withkobalte TSX Page

With the ARIA attributes all set up for you, your life as a developer is much easier. Furthermore, the granularity of the components makes development and maintenance easier.

Using advanced Kobalte components

Now that we’re familiar with some basics of Kobalte, we can briefly go over some of the more advanced components. In KobalteAdvanced.tsx, we have the following components: Progress Bar, Hover Card, Popover, and Tabs.

All of these components are both granular and composable, just like the TextField that we saw earlier. Below is the ProgressBar code:

      <h2>Progress Bar</h2>
      <Progress.Root value={80} class="progress">
          <div class="progress__label-container">
              <Progress.Label class="progress__label">
              <Progress.ValueLabel class="progress__value-label" />
          <Progress.Track class="progress__track">
              <Progress.Fill class="progress__fill" />

Notice that in this case, I’m using custom CSS classes. You could just as easily integrate with a CSS framework. Below is the HoverCard component:

                <h2>Hover Card (move mouse over to see it)</h2>
                        <HoverCard.Content class="hovercard__content">
                            <HoverCard.Arrow />
                                alt="Fabien MARIE-LOUISE"
                            <h2 class="hovercard__title">
                                Fabien MARIE-LOUISE
                            <p class="hovercard__description">
                                Developer and UI Design enthusiast. Building UI
                                related stuffs for @solid_js

The Popover component is similar; you could easily install this popover component into your frontend. Often times, libraries don’t have a popover component specifically, so you’d have to create one yourself. This is definitely another advantage of using Kobalte:

    <h2>Popover (click open to see it)</h2>
        <Popover.Trigger class="popover__trigger">
            <Popover.Content class="popover__content">
                <Popover.Arrow />
                <div class="popover__header">
                    <Popover.Title class="popover__title">
                        About Kobalte
                    <Popover.CloseButton class="popover__close-button">
                <Popover.Description class="popover__description">
                    A UI toolkit for building accessible web apps
                    and design systems with SolidJS.

Finally, the Tabs component is very similar to what you would see in other frontend libraries. Again, the composition capabilities make it great to use in any app:

    <Tabs.Root aria-label="Main navigation" class="tabs">
        <Tabs.List class="tabs__list">
            <Tabs.Trigger class="tabs__trigger" value="profile">
            <Tabs.Trigger class="tabs__trigger" value="dashboard">
            <Tabs.Trigger class="tabs__trigger" value="settings">
            <Tabs.Trigger class="tabs__trigger" value="contact">
            <Tabs.Indicator class="tabs__indicator" />
        <Tabs.Content class="tabs__content" value="profile">
            Profile details
        <Tabs.Content class="tabs__content" value="dashboard">
            Dashboard details
        <Tabs.Content class="tabs__content" value="settings">
            Settings details
        <Tabs.Content class="tabs__content" value="contact">
            Contact details


In this article, we explored getting started with Kobalte by running through a few examples. I demonstrated how to create your first component, and then we covered some more advanced use cases. Kobalte is simple to integrate into your Solid applications, and building applications is even easier when the ARIA Standards are already set up for you. I recommend checking out my sample repo, as well as the Kobalte docs.

Thanks for reading! Follow my writing on and Twitter at @AndrewEvans0102.

Are you adding new JS libraries to improve performance or build new features? What if they’re doing the opposite?

There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.

LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.

LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Build confidently — .

Andrew Evans Husband, engineer, FOSS contributor, and developer at AWS. Follow me at and

Leave a Reply