DEV Community

Cover image for Building UIs with Franken UI, a Shadcn alternative
Megan Lee for LogRocket

Posted on • Edited on • Originally published at blog.logrocket.com

Building UIs with Franken UI, a Shadcn alternative

Written by Jude Miracle✏️

As developers, we often look for tools and ways to enhance our productivity and improve our workflows. Using pre-built UI components is one effective way to achieve this.

In this article, we will look at Franken UI, an open source library of pre-built UI components that takes inspiration from Shadcn UI's design principles but is built to integrate closely with HTML and UIkit 3. We will provide an in-depth overview of Franken UI, highlighting its unique benefits and features. We will also explore its components and how to use them in our applications.

What is Franken UI?

Franken UI is an open source library that provides sets of pre-built UI components built with an HTML-first approach, making them adaptable and easy to integrate into an existing project. It was also developed to be framework-agnostic, meaning that it can be integrated into JavaScript frameworks like Vue, React, or Svelte.

Franken UI is compatible with UIkit 3 and can work as a standalone CSS framework but can be integrated with Tailwind CSS for faster styling and customization. The design of Franken UI is influenced by shadcn/ui. It aims to provide a solution to developers who are not comfortable using React, Vue, or Svelte by leveraging UIkit for JavaScript and accessibility.

Created by an independent developer, Sveltecult, Franken UI is still relatively new but is gradually gaining popularity among developers. At the time of writing, it has just over 1K GitHub stars.

Benefits of using Franken UI

  • HTML-first approach: Franken UI is designed with an HTML-first approach, which makes it accessible, easy to use, and easily integrated into existing projects without requiring an in-depth knowledge of JavaScript. This approach helps simplify the development process, especially for developers comfortable using only HTML and CSS
  • Framework-agnostic: Franken UI can be used with any major JavaScript framework, such as Vue, React, or Svelte, making it adaptable to a wide range of development environments. This allows developers to use Franken UI alongside their preferred frameworks without compatibility issues
  • Tailwind CSS plugin: Franken UI components work as standalones or as Tailwind CSS plugins, leveraging the utility-first approach of Tailwind CSS for styling. This integration provides benefits such as IntelliSense for class names and automatic removal of unnecessary CSS
  • Beautifully designed components: Inspired by Shadcn UI, Franken UI includes a collection of beautifully designed, reusable, and customizable components that you can easily copy into your project
  • Compatibility with UIkit 3 and icons: Franken UI is not fully compatible with UIkit 3, but it leverages UIkit JavaScript and its accessibility features when building interactive UIs. Franken UI allows users to choose which dependencies and features to include in their applications by making UIkit JavaScript and icons optional
  • Accessibility: Franken UI was developed with accessibility in mind and supports WAI-ARIA, ensuring that the components adhere to accessibility features. This means anyone can use it, including those with disabilities. It also supports internationalization and allows global or per-instance customizations
  • Open source: Franken UI is open source software anyone can use and contribute to. Additionally, it allows you to modify the source code of the components to fit your specific needs or create custom components based on the existing ones

Franken UI features

Franken UI offers a variety of features that you can use to improve your development experience. Below, we will explore some of these features, including hooks, dark mode, theming, multiple reusable components, and more.

Theming and the Franken UI theme generators

Franken UI offers a flexible system for theming that enables developers to create consistent and customizable UIs. It provides customizable theming options through CSS variables and Tailwind CSS utility classes.

Franken UI theming follows a simple convention for color theming, using background and foreground variables, where the background variable is used to apply the background color of the component, and the foreground variable is used for the text color. To apply these themes, for example, you would use classes like text-primary-foreground and bg-primary:



<div class="bg-background text-foreground"></div>


Enter fullscreen mode Exit fullscreen mode

There are many variables available for customization that you can copy and paste into your project:



Card backgrounds:
  - --card: 0 0% 100%;
  - --card-foreground: 222.2 47.4% 11.2%;
Popover backgrounds:
  - --popover: 0 0% 100%;
  - --popover-foreground: 222.2 47.4% 11.2%;
Border colors:
  - --border: 214.3 31.8% 91.4%;
Input borders:
  - --input: 214.3 31.8% 91.4%;


Enter fullscreen mode Exit fullscreen mode

To add new colors, you would update your CSS and tailwind.config.js file:



:root {
  --warning: 38 92% 50%;
  --warning-foreground: 48 96% 89%;
}

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  theme: {
    extend: {
      colors: {
        warning: "hsl(var(--warning))",
        "warning-foreground": "hsl(var(--warning-foreground))",
      },
    },
  },
};


Enter fullscreen mode Exit fullscreen mode

You can now use these custom colors in your components:



<div class="bg-warning text-warning-foreground"></div>


Enter fullscreen mode Exit fullscreen mode

Franken UI also offers a variety of themes, including zinc, slate, red, gray, blue, etc. To set a theme, you need to configure the presetQuick() function or use the hooks() and variables() functions to use your desired theme:



// presetQuick function
import presetQuick from "franken-ui/shadcn-ui/preset-quick";

/** @type {import('tailwindcss').Config} */
export default {
  presets: [presetQuick({ theme: "green" })],
};

// hooks and variables
import variables from "franken-ui/shadcn-ui/variables";
import hooks from "franken-ui/shadcn-ui/hooks";

const shadcn = hooks({
  theme: "green",
});

/** @type {import('tailwindcss').Config} */
export default {
  plugins: [
    variables({
      theme: "green",
    }),
  ],
};


Enter fullscreen mode Exit fullscreen mode

Franken UI also supports theme generators, allowing users to create custom themes from scratch. Use tools like ZippyStarter, Oxidus, or https://ui.jln.dev/ to generate a custom theme, then copy the CSS, convert it to an object using https://transform.tools/css-to-js, and then configure Tailwind by updating tailwind.config.js to include your custom palette.

Dark mode with dark mode switcher

Franken UI supports dark mode by leveraging Tailwind CSS’ dark mode functionality, which its components support using the "class" option. Opting for the presetQuick() function ensures everything is automatically set up for you.

You can create a dark mode switcher by adding JavaScript that toggles themes based on user choices. To implement dark mode in HTML:

  • Check user preference. Add a script to apply the user's color scheme preference
  • Create a toggle button. Add SVG icons for light/dark mode toggle
  • Handle click events. Update theme preference in local storage and toggle a dark class on the HTML element

Here’s a sample implementation:



<head>
  <script>
    if (
      localStorage.getItem("color-theme") === "dark" ||
      (!("color-theme" in localStorage) &&
        window.matchMedia("(prefers-color-scheme: dark)").matches)
    ) {
      document.documentElement.classList.add("dark");
    } else {
      document.documentElement.classList.remove("dark");
    }
  </script>
</head>
<body>
  <button class="uk-button uk-button-primary">Button</button>
  <button id="theme-toggle">
    <span
      id="theme-toggle-light-icon"
      uk-icon="happy"
      class="dark:hidden"
    ></span>
    <span
      id="theme-toggle-dark-icon"
      uk-icon="star"
      class="hidden dark:block"
    ></span>
  </button>
  <script>
    var themeToggleBtn = document.getElementById("theme-toggle");
    themeToggleBtn.addEventListener("click", function () {
      if (localStorage.getItem("color-theme")) {
        if (localStorage.getItem("color-theme") === "light") {
          document.documentElement.classList.add("dark");
          localStorage.setItem("color-theme", "dark");
        } else {
          document.documentElement.classList.remove("dark");
          localStorage.setItem("color-theme", "light");
        }
      } else {
        if (document.documentElement.classList.contains("dark")) {
          document.documentElement.classList.remove("dark");
          localStorage.setItem("color-theme", "light");
        } else {
          document.documentElement.classList.add("dark");
          localStorage.setItem("color-theme", "dark");
        }
      }
    });
  </script>
</body>


Enter fullscreen mode Exit fullscreen mode

Hooks

Franken UI provides hooks that help simplify customization by replacing or adding properties to predefined selectors, allowing for global changes without repetitive code. Hooks can be used through the presetQuick function for quick setup or the preset function for more control:



// with presetQuick function
import presetQuick from "franken-ui/shadcn-ui/preset-quick";

/** @type {import('tailwindcss').Config} */
export default {
  presets: [
    presetQuick({
      overrides: {
        accordion: {
          "hook-accordion": {},
          "hook-item": {
            background: "purple",
          },      },
    }),
  ],
};

// with preset function
import preset from "franken-ui/shadcn-ui/preset";
import variables from "franken-ui/shadcn-ui/variables";
import ui from "franken-ui";

/** @type {import('tailwindcss').Config} */
export default {
  presets: [preset],
  plugins: [
    variables(),
    ui({
      components: {
        accordion: {
          "hook-accordion": {},
        },
      },
    }),
  ],
};


Enter fullscreen mode Exit fullscreen mode

You can customize components like accordions and alerts and apply component states like hover or focus by setting properties or using Tailwind's @apply directive.

You can opt out of shadcn/ui for a custom theme or create custom selectors using hook-misc for additional styling needs. Hooks ensure consistency, reduce build size, and keep HTML code clean.

Multiple reusable components

Franken UI provides a collection of reusable components that can be easily copied and pasted into your project without writing your code from scratch. It has over 70 pre-built components including Form, Card, Modal, Progress, Thumbnav, and examples of how to use them.

JavaScript integration with Franken UI

Because of its compatibility with the UIkit framework, JavaScript and Franken UI work together to enable dynamic and interactive component use. Components can be initialized in HTML or JavaScript and integrate well with frameworks like Vue.js and React.

To use Franken UI components, add the required JavaScript files to your HTML <head> tag using the CDN, and give components the uk-* or data-uk-* attributes. Use the data-uk-* prefix for React:



<div uk-sticky="offset: 50;"></div>
// React
<div data-uk-sticky="offset: 50;"></div>


Enter fullscreen mode Exit fullscreen mode

Franken UI components also emit events, which allow custom code to be executed.

Franken UI components

Before getting started, check out the installation guide for information on installing Franken UI via CDN or npm and other customization setups like theming.

Good! Now let’s explore different Franken UI components.

Button

Franken UI provides a range of button components with various styles that are easy to use and customize. To use the button component, apply the .uk-button class to <a> or <button> elements, along with style modifiers like .uk-button-default, .uk-button-primary, or .uk-button-danger:



<button className="uk-button uk-button-default">Default</button>
<button className="uk-button uk-button-primary">Primary</button>
<button className="uk-button uk-button-danger">Danger</button>


Enter fullscreen mode Exit fullscreen mode

Three Buttons For Default, Primary, And Danger Buttons can be adjusted to full width using the .uk-width-1-1 class or Tailwind CSS utility classes. It supports dropdowns and various states like hover, focus, and disabled.

Forms

Franken UI offers form components that are used to create functional and aesthetically pleasing forms. Its form elements include text inputs, dropdown menus, text areas, radio buttons, checkboxes, and range sliders, and are built with accessibility in mind, using appropriate ARIA attributes to ensure they are usable by all users:



<form className="grid gap-2 w-80 p-3 shadow-sm bg-white">
  <input className="uk-input" type="text" placeholder="name" />
  <select className="uk-select">
    <option>item 1</option>
    <option>item 2</option>
  </select>
  <textarea className="uk-textarea" placeholder="text area"></textarea>
  <input className="uk-radio" type="radio" />
  <input className="uk-checkbox" type="checkbox" />
  <input className="uk-range" type="range" />
</form>


Enter fullscreen mode Exit fullscreen mode

Franken UI includes form state modifiers, such as danger state and disabled state using the uk-form-danger class and disable attribute, to enhance user experience and it also has style for stacked, horizontal, and grid form layouts. The following is an example of what the stacked layout looks like: Franken UI Stacked Layout

Franken UI modals

Franken UI provides a modal component that enables developers to create interactive dialog boxes in their applications. The modals can be completely customized with Tailwind CSS classes and other UIkit features.

To create a modal, you need to define the modal structure and use the uk-modal attribute or data-uk-modal for React to enable its functionality:



<button className="uk-button uk-button-default" type="button" data-uk-toggle="target: #modal-example">
  Open Modal
</button>
<div id="modal-example" data-uk-modal>
  <div className="uk-modal-dialog uk-modal-body">
    <h2 className="uk-modal-title">Modal Title</h2>
    <p>This is the content of the modal.</p>
    <p className="uk-text-right">
      <button className="uk-button uk-modal-close" type="button">Close</button>
      <button className="uk-button uk-button-primary" type="button">Save</button>
    </p>
  </div>
</div>


Enter fullscreen mode Exit fullscreen mode

The Franken UI Modal Component The Franken UI modal component also allows you to define the position of the close button depending on your design requirement. You either position it on the inside or outside of your modal dialog using uk-modal-close-default or uk-modal-close-outside, respectively.

You can modify the modal's appearance, functionality, and size. For example, to make the modal fit your design requirements, you can change its width, and alignment, add animations, and more.

The Franken UI Card component

Franken UI's Card component is used to create visually appealing layout boxes. It can be defined using the uk-card class, with additional classes like uk-card-body and uk-card-title for padding and titles:



<div className="uk-card uk-card-default w-1/4">
  <div className="uk-card-header">
    <h3 className="uk-card-title">Card Title</h3>
  </div>
  <div className="uk-card-body">
    <p>
       sit amet, consectetur adipiscing elit, sed
        do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    </p>
  </div>
  <div className="uk-card-footer">
    <a href="#" className="uk-button uk-button-default">Read more</a>
  </div>
</div>


Enter fullscreen mode Exit fullscreen mode

The card structure can include a header, body, and footer for effective content organization. Franken UI offers several style modifiers for cards, including uk-card-default for standard style, uk-card-primary for emphasized primary color, uk-card-secondary for secondary background color, and uk-card-danger for destructive background color: The Franken UI Card component

Franken UI Icon component

Franken UI also provides a versatile and easy-to-use Icon component. The Icon component allows you to integrate SVG icons in your content and can be styled using Tailwind CSS for a consistent design experience. To implement the Franken UI icon, simply add the uk-icon attribute to a <span> or <a> element and specify the desired icon name:



<span uk-icon="icon: happy"></span>

<a href="" uk-icon="star"></a>


Enter fullscreen mode Exit fullscreen mode

The icon option also specifies the icon that will be displayed, with a wide range of icons available in the Franken UI library, covering categories such as apps, devices, storage, brands, editors, directions, and more.

Icons can be resized using the ratio option and styled using CSS or Tailwind CSS to match text color. Various modifiers, like uk-icon-link, uk-icon-button and uk-icon-image, allow for different styles and uses, such as transforming icons to links, creating icon buttons, or scaling background images to icon size:



// link modifier
<a href="" class="uk-icon-link" uk-icon="cart"></a>

// button modifier
<a href="" class="uk-icon-button" uk-icon="sign-out"></a>

// image modifier
<span 
    class="uk-icon uk-icon-image" 
    style="background-image: url(/images/dark.jpg)">
</span>


Enter fullscreen mode Exit fullscreen mode

Building a simple application with Franken UI

In this section, we will build a simple application with an interactive modal, a form with validation, and a responsive navigation bar using some of the Franken UI components.

Creating a responsive navigation bar



const Navbar = () => {
  return (
    <nav className="uk-navbar-container " data-uk-navbar>
      <div className="uk-container w-full mx-auto flex">
        <h1 className="uk-navbar-left">
          <a href="#" className="uk-navbar-item uk-logo text-2xl font-bold">
            Logo
          </a>
        </h1>
        <div className="uk-navbar-center">
          <ul className="uk-navbar-nav hidden md:flex space-x-4">
            <li className="uk-active">
              <a href="#" className="text-gray-700 hover:text-gray-900">
                Home
              </a>
            </li>
            <li>
              <a href="#" className="text-gray-700 hover:text-gray-900">
                Features
              </a>
              <div className="uk-navbar-dropdown">
                <ul className="uk-nav uk-navbar-dropdown-nav">
                  <li className="uk-active">
                    <a href="#">Feature 1</a>
                  </li>
                  <li>
                    <a href="#">Feature 2</a>
                  </li>
                  <li>
                    <a href="#">Feature 3</a>
                  </li>
                </ul>
              </div>
            </li>
            <li>
              <a href="#" className="text-gray-700 hover:text-gray-900">
                Pricing
              </a>
            </li>
            <li>
              <a href="#" className="text-gray-700 hover:text-gray-900">
                Contact
              </a>
            </li>
          </ul>
        </div>
        <div className="uk-navbar-right">
          <div className="uk-navbar-nav hidden md:flex space-x-4">
            <button className="uk-button uk-button-primary">Sign in</button>
          </div>
          <button
            className="md:hidden uk-navbar-toggle"
            data-uk-navbar-toggle-icon
          ></button>
          <div className="uk-navbar-dropdown md:hidden">
            <ul className="uk-nav uk-navbar-dropdown-nav">
              <li className="uk-active">
                <a href="#">Home</a>
              </li>
              <li>
                <a href="#">Features</a>
              </li>
              <li>
                <a href="#">Pricing</a>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </nav>
  );
};
export default Navbar;


Enter fullscreen mode Exit fullscreen mode

Here is the responsive navbar built with the code above: Responsive Navbar

Implementing a form with validation



// Form.tsx
import React, { useState } from "react";

const Form = () => {
  const [formData, setFormData] = useState({ name: "", message: "" });
  const [errors, setErrors] = useState({ name: "", message: "" });

  const handleChange = (
    event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
  ) => {
    const { name, value } = event.target;
    setFormData({ ...formData, [name]: value });
    setErrors({ ...errors, [name]: "" }); // Clear the error message when user starts typing
  };

  const validate = () => {
    let valid = true;
    const newErrors: { name: string; message: string } = {
      name: "",
      message: "",
    };

    if (!formData.name) {
      newErrors.name = "Name is required";
      valid = false;
    }

    if (!formData.message) {
      newErrors.message = "Message is required";
      valid = false;
    }

    setErrors(newErrors);
    return valid;
  };

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (validate()) {
      console.log("form submitted");
    }
  };

  return (
    <>
      <form
        onSubmit={handleSubmit}
        className="grid gap-2 w-80 p-3 shadow-sm bg-white"
      >
        <legend className="uk-legend">Example Form</legend>
        <input
          className="uk-input"
          type="text"
          name="name"
          placeholder="Name"
          value={formData.name}
          onChange={handleChange}
        />
        {errors.name && <div className="uk-text-danger">{errors.name}</div>}
        <textarea
          className="uk-textarea"
          name="message"
          placeholder="Leave a message"
          value={formData.message}
          onChange={handleChange}
        ></textarea>
        {errors.message && (
          <div className="uk-text-danger">{errors.message}</div>
        )}
        <div className="uk-margin">
          <button type="submit" className="uk-button uk-button-primary">
            Submit
          </button>
        </div>
      </form>
    </>
  );
};

export default Form;


Enter fullscreen mode Exit fullscreen mode

Notice that we are only console logging "form submitted". In the next section, we will look at how to notify the user that the form has been submitted using a Franken UI modal.

Adding interactive modals

Let’s install UIkit into our project to help us with the JavaScript functionality of opening the modal after the form has been submitted:



npm install uikit @types/uikit


Enter fullscreen mode Exit fullscreen mode

Create a Modal file and add the following code below:



// Modal.tsx
const Modal = () => (
  <div id="exampleModal" className="uk-flex-top uk-modal" data-uk-modal>
    <div className="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
      <h2 className="uk-modal-title">Modal Title</h2>
      <p>Form has been submitted successfully!</p>
      <p className="uk-text-right">
        <button className="uk-button uk-modal-close" type="button">
          Close
        </button>
      </p>
    </div>
  </div>
);
export default Modal;


Enter fullscreen mode Exit fullscreen mode

Import the UIkit and Modal components in your Form component and add them below the form tag. Then update the handleSubmit function to trigger the modal to open after the form has been submitted:



const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
  event.preventDefault();
  if (validate()) {
    UIkit.modal("#exampleModal").show();
  }
};


Enter fullscreen mode Exit fullscreen mode

Interactive Modals With UIkit

Franken UI vs. Shadcn UI

Franken UI and Shadcn UI are both design systems that provide aesthetic and functional UI components, but they have different approaches:

Franken UI Shadcn UI
Implementation Framework-agnostic (HTML/CSS) Framework integration (React, Vue, Svelte)
Customization Low-level (CSS classes, hooks) Built-in (within framework)
Learning curve Easier (HTML/CSS) Requires framework knowledge
Community support Smaller community and limited resources Larger community, quick access to resources, faster development cycles
Use case Small teams, solo developers, framework-independent projects Rapid development within a framework

Conclusion

Franken UI stands out as a good alternative to Shadcn UI, offering a comprehensive library of pre-built components that significantly streamline the web development process. Its HTML-first approach, coupled with seamless integration of Tailwind CSS and emphasis on accessibility, ensures that developers can create modern, responsive user interfaces with ease.

Franken UI is still a relatively new UI solution and has been updated with more components and features. Drop a comment on your thoughts about this new tool.

LogRocket: Full visibility into your web and mobile apps

LogRocket Signup

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

Try it for free.

Top comments (1)

Collapse
 
bernert profile image
BernerT

How does Franken UI handle component-specific state management compared to other UI libraries?