Library / SDK
bluzky/salad_ui avatar
bluzky/salad_ui

SaladUI: shadcn-style components for Phoenix LiveView

Phoenix Liveview component library inspired by shadcn UI

1,057 stars73 forksElixirMIT

At a glance

What is it?
SaladUI is a Phoenix LiveView component library modelled on shadcn/ui, with two installation modes: a mix salad.setup that uses the package directly, and mix salad.install that copies component source into your app so you own it. The v1 component set ships without unit tests, and the author says so in the README.
Who is it for?
Adopt SaladUI if you are building a Phoenix LiveView app and want shadcn-style components with Tailwind already wired, and if you prefer owning copied source to tracking a dependency. Do not adopt it if a missing unit test suite is a blocker for your team, because the README states the v1 components are not covered.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 73 days ago.
What is it written in?
Mainly Elixir, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A shadcn port that keeps Phoenix naming

SaladUI is described as a collection of Live View components inspired by shadcn. The inspiration is the whole point, and its local install mode copies the components into your project as source rather than as a sealed package, so you can change a button and keep the change. The port adapts that idea to Elixir and Phoenix rather than copying the React code.

The adaptation is visible in the component call syntax. Instead of JSX, you get HEEx function components with a lowercase dot prefix, so a button is written as.button and a dialog opens a.dialog element containing a.dialog_content element, and the modules are imported with import SaladUI.Button when you use the library mode, or import MyAppWeb.Components.UI.Button when you use the local install mode.

The project is MIT licensed, written in Elixir, and published to Hex as salad_ui. The storybook demo is hosted at salad-storybook.fly.dev, and the repository carries a storybook/ directory of its own for development. The package.json at the repository root describes a JavaScript side to the library as well, with the assets split into core/ and components/ directories and exports mapped for the root, the components, and the core.

The credits list names the inspirations precisely: tailwind css for the utility styling, turboprop for the Tailwind class merging that was borrowed, shadcn/ui as the design source, and the Phoenix Framework as the host.

Two installation modes with different ownership

The dependency goes into mix.exs like any Hex package:

elixir
def deps do
  [
    {:salad_ui, "~> 1.0.0-beta.3"},
  ]
end

After that you pick a mode, and the README is unusually direct about the difference. The library mode is a single command:

bash
mix salad.setup

It sets up SaladUI to use components directly from the library, so you can import them immediately. The documentation lists its properties as ready to use, with Tailwind CSS and color schemes configured, but also lists two things it cannot do: components cannot be customized, and it uses external package dependencies.

The local mode copies files into your project:

bash
# Default installation
mix salad.install

# With custom prefix and color scheme
mix salad.install --prefix MyUI --color-scheme slate

The copied components land under lib/my_app_web/components/ui/, and the local mode adds full customization, copies all JavaScript files locally, carries no external runtime dependencies, and supports custom module prefixes. The trade-off is that the code is now yours, which means upstream fixes do not arrive on their own, and the prefix option means the import path changes to MyAppWeb.Components.UI.Button.

Both modes are the same idea as shadcn's, applied to a compiled language. Neither choice is free, and the deciding question is whether you want the library to keep the components or you do.

Using the components in a live view

The usage example is a standard Phoenix live view. The module uses your app's :live_view macro, imports the two component modules, and renders them in HEEx:

elixir
defmodule MyAppWeb.PageLive do
  use MyAppWeb, :live_view
  import SaladUI.Button
  import SaladUI.Dialog

  def render(_) do
    ~H"""
    <.button>Click me</.button>
    <.dialog id="my-dialog">
      <.dialog_content>
        <p>Hello world!</p>
      </.dialog_content>
    </.dialog>
    """
  end
end

The import of individual component modules rather than one umbrella module is worth noticing. It means a page that uses a button and a dialog imports two modules, and the compiler will not warn you about the ones you did not import. In the local install mode the same file changes only in the import lines, from SaladUI.Button to MyAppWeb.Components.UI.Button, which is the practical difference between the two modes in a real diff.

The components use the function component style throughout, with the element name carrying the meaning, so a dialog needs an id and the content lives in a child component rather than in an attribute. For developers coming from Phoenix core_components, which use attribute-based tags, the switch to a dot-prefixed function component with nested children is the adjustment to plan for.

What the component table leaves empty

The README includes a component matrix with a column for v0 and a column for v1, which is the most informative table in the project because it shows both coverage and the gap. Components present in both columns include Accordion, Alert, Alert Dialog, Avatar, Badge, Breadcrumb, Button, Card, Checkbox, Collapsible, Dialog, Dropdown Menu, Form, Hover Card, Input, Label, Pagination, Popover, Progress, Radio Group, Scroll Area, Select, Separator, Sheet, Skeleton, Slider, Switch, Table, Tabs, Textarea, and Tooltip.

Five components are marked as absent in both the v0 and the v1 column: Carousel, Combobox, Context Menu, Drawer, and Input OTP. The Command component is marked as new in v1 with a contributor credited. What this means in practice is that a v1 project should be planned around the v1 column, and the missing components are simply not on the menu.

The matrix is also a map of the library's ambition. A component library that mirrors shadcn's catalogue will always be behind it, and the v0 to v1 jump suggests a deliberate redesign of the component internals rather than an incremental addition, which is a common pattern in a library that started as a copy and then became its own thing.

The naming of the columns also implies the versioning story, and it connects to the release list. The most recent published Hex release is 0.9.0, from 2024-10-19, while the dependency snippet in the README asks for ~> 1.0.0-beta.3. The 1.x line is available as a pre-release.

Tailwind, color schemes, and the JavaScript side

Both installation modes handle the same two chores: setting up Tailwind CSS and configuring color schemes. The library mode also configures the JavaScript hooks and components that go with the interactive parts, while the local mode copies all the JavaScript files into your project alongside the Elixir modules.

The JavaScript layer is a separate npm package manifest in the repository, with the name salad_ui and version 1.0.0, whose files list includes priv/static/*, assets/*, assets/salad_ui/*, assets/salad_ui/core/*, and assets/salad_ui/components/*. The exports map the package root to assets/salad_ui/index.js, and expose the component and core directories as wildcard subpaths, which is the pattern that lets a bundler import only the JavaScript a page needs.

The scripts in that manifest show how the front end is worked on and checked. Formatting and linting run through npm --prefix assets, and the test script runs node --test against assets/test/**/*.test.mjs, using the Node built-in test runner on .mjs files rather than a separate test framework. The Elixir side sits alongside with mix.exs, mix.lock, a .formatter.exs, config/, assets/, priv/, and test/ directories, and the repository also carries a CHANGELOG.md, a flake.nix, and a .envrc for development environment setup.

There is one configuration hook worth knowing, for error pages rather than components:

elixir
config :salad_ui, :error_translator_function, {MyAppWeb.CoreComponents, :translate_error}

That points the library at your own translate_error implementation, so form and validation errors keep the wording and markup your application already uses.

The unit test gap the author puts in writing

This is the limitation that most affects an adopter, and the README states it without hedging. Under a heading for unit testing, the note reads that all v1 components are not covered by unit tests, and that the author is working on an important project and does not have much time, with an invitation to contribute tests.

The Elixir side still has a test/ directory and the CI badge for a tests workflow, so testing infrastructure exists, but the v1 component set is the gap. For a component library, the risk is not dramatic. These are declarative markup components, and the main failure modes are a changed attribute contract or a broken render, which a test would catch quickly. The practical exposure is that a refactor of a component in a version bump can change behaviour without a failing test telling you.

The JavaScript side is in a better position, since the package manifest wires node --test against the assets test files. The interactive components, which rely on JavaScript hooks rather than markup alone, are where behaviour is most likely to break, and that is where the runner already points.

Given the alpha-style version, 1.0.0-beta.3, and the last push to the repository on 2026-08-11, a team adopting this should budget for reading the diff when a new version lands rather than trusting the test suite to catch it.

Against Phoenix core_components and against the raw library

The baseline comparison for any Phoenix team is the core_components module that Phoenix itself ships. Those are a small set of form and layout components with a Phoenix house style, useful when you need a select, an input, and a table with a consistent look and nothing more. SaladUI's difference in approach is breadth and origin: a catalogue of roughly thirty components following shadcn's visual and structural conventions, styled with Tailwind utilities, and delivered in two ownership modes rather than one.

That difference cuts both ways. If your application already has a design system built on core_components, adding SaladUI means two visual languages unless you theme one into the other, and the Tailwind class merging that was borrowed from turboprop exists precisely to make that theming work. If your application does not have a design system and you want something that looks like a modern SaaS product on day one, SaladUI is the shorter path.

The other comparison is between SaladUI's own two modes, which is the real decision this library asks you to make. The library mode gives you zero-copy upgrades and no customization. The local mode gives you total control and no automatic upgrades. The README's own summary of the two modes is the honest version of the trade-off, and the deciding factor is how often you expect to need a change that upstream makes for you.

The shadcn comparison also carries a caveat. SaladUI is a port of an idea, not a mirror of a package, and the version matrix shows its own v0 to v1 redesign. Expect to implement missing pieces yourself rather than wait for them to appear upstream.

Editorial conclusion

Adopt SaladUI if you are building a Phoenix LiveView app and want shadcn-style components with Tailwind already wired, and if you prefer owning copied source to tracking a dependency. Do not adopt it if a missing unit test suite is a blocker for your team, because the README states the v1 components are not covered. Verify first that mix salad.install with the prefix and color scheme options produces the module names you intend to import, since that rename happens in your own tree.

Frequently asked questions

How do I install SaladUI in a Phoenix application?

Add the dependency to mix.exs with {:salad_ui, "~> 1.0.0-beta.3"}, then choose an installation method. mix salad.setup uses the components straight from the library, and mix salad.install copies all component and JavaScript source into your project under lib/my_app_web/components/ui/ for customization.

What is the difference between mix salad.setup and mix salad.install?

The setup command wires Tailwind CSS, color schemes, and JavaScript hooks so components work immediately, but components cannot be customized and it relies on external package dependencies. The install command copies all component source and JavaScript locally, allows full customization, adds no external runtime dependencies, and supports custom module prefixes.

Which components does SaladUI v1 include?

The README's component matrix lists v1 coverage for Accordion, Alert, Alert Dialog, Avatar, Badge, Breadcrumb, Button, Card, Checkbox, Collapsible, Command, Dialog, Dropdown Menu, Form, Hover Card, Input, Label, Pagination, Popover, Progress, Radio Group, Scroll Area, Select, Separator, Sheet, Skeleton, Slider, Switch, Table, Tabs, Textarea, and Tooltip. Carousel, Combobox, Context Menu, Drawer, and Input OTP are marked as absent in both columns.

Are the SaladUI v1 components covered by unit tests?

No. The README states that all v1 components are not covered by unit tests, and asks contributors to help add them. The JavaScript assets do have a test script that runs node --test against assets/test/**/*.test.mjs.

How do I use a custom error translation with SaladUI?

Set the :error_translator_function option in your configuration, pointing at your own implementation, for example config :salad_ui, :error_translator_function, {MyAppWeb.CoreComponents, :translate_error}. The install task also accepts a prefix and a color scheme, such as mix salad.install --prefix MyUI --color-scheme slate.

Official sources

  1. bluzky/salad_ui on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bluzky-salad-ui.svg)](https://hysenlabs.com/projects/bluzky-salad-ui)

Was this useful?