netoum

netoum

Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and LiveView hooks.
It works with both Phoenix Controllers and LiveView without requiring a JavaScript framework or Node.js build process.

Currently in early alpha, looking for feedback on the architecture, API design, and overall approach

History

I originally created corex-ui.com, a Vanilla JS integration of Zag.js for static websites. The challenge was adapting this approach to Phoenix’s server-rendered model while feeling natural to Phoenix developers. Corex is the result: interactive, accessible components that work with Phoenix conventions rather than against them.

Why Corex

State Machines for Complex Interactions

Zag.js handles intricate state management and accessibility concerns. An accordion must manage which items are open/closed, keyboard navigation, focus management, ARIA attributes, and animation states. Rather than implementing this yourself, Zag.js provides battle-tested state machines.

Seamless Phoenix Integration

Corex wraps Zag.js with ergonomic Phoenix components:

Manual Slot

<.accordion>
  <:trigger value="anatomy">Anatomy</:trigger>
  <:trigger value="machine">State machines</:trigger>
  <:content value="anatomy">Structure & slots</:content>
  <:content value="machine">Zag.js on the client</:content>
</.accordion>

With List

<.accordion
  class="accordion"
  items={
Corex.Content.new([
  %{trigger: "Anatomy", content: "Structure & slots"},
  %{trigger: "State machines", content: "Zag.js on the client"}
])
  }
/>

API Control and Events

Control components from client or server:
Client

<.action phx-click={Corex.Accordion.set_value("my-accordion", ["item-1"])}>
  Open Item 1
</.action>

Server

def handle_event("open_item", _, socket) do
  {:noreply, Corex.Accordion.set_value(socket, "my-accordion", ["item-1"])}
end

Unstyled by Default

Components ship with zero styling. They expose semantic data attributes you can target with your own CSS:

[data-scope="accordion"][data-part="item-trigger"] {
  /* Your styles */
}

[data-scope="accordion"][data-part="item-trigger"][data-state="open"] {
  /* Open state styles */
}

Works with any design system without style overrides or specificity battles.

Simple by Design

Installation is straightforward:

use Corex
import Hooks from "corex"

const liveSocket = new LiveSocket("/live", Socket, {
  hooks: {...colocatedHooks, ...Hooks}
})

Progressive Enhancement

Uncontrolled by default: Components manage their own state on the client using Zag.js. User interactions update the UI immediately without server round-trips. Covers most use cases.

Controlled when needed: The server owns the state. State changes emit as events and reflect back through assigns. Useful when component state must be validated, persisted, or coordinated with application logic.

def mount(_params, _session, socket) do
  {:ok, assign(socket, :value, ["item-1"])}
end

def handle_event("on_value_change", %{"value" => value}, socket) do
  {:noreply, assign(socket, :value, value)}
end

Both modes expose the same interaction API and can be mixed within the same application.

Forms and Validation

Integrates with Phoenix forms without custom abstractions. Components work without server validation (client-managed state) or with changesets (server-side validation). Form fields, labels, and errors are passed explicitly through slots.

Feedback, and suggestions welcome as I continue developing this library.

Documentation

Corex Demo
Corex Hex Doc
Corex Hex PM
Github:

Showing Posts 1 to 10

achempion

achempion

Looking great, congrats on the launch!

felix-starman

felix-starman

Thanks for doing this!

I have been hoping to see something based on ZagJS for a while and I’m very thankful you’ve taken this on. I’m excited to see this grow :blush:

Eiji

Eiji

Do you have plans for 3-state switch? That’s a feature which is often missed, but very helpful.

Do you support / or plan to support “native” HTML5 + CSS3 solutions (that does not require JavaScript at all). See below link for an accordion example:

A huge :+1: for making components accessible by default - it’s a huge help for developers!

netoum

netoum OP

There is currently no plan to have other interactive component than Zagjs
Mainly because they come packed with accessibility features that would be hard to replicate and test
Let’s take an example:
The accordion has Direction (LTR/RTL) and an orientation (Vertical/Horizontal).
Some items may be disabled
The user can navigate from one Item to the previous/next with keyboard arrows.
The logic will change depending on the combinaison of all props and will skip the disabled items

You can see an example in the e2e app on the Accordion Playground /playground/accordion

Thank you for the support

netoum

netoum OP

Phoenix Form Support

Corex now supports Phoenix forms for Checkbox, Select and Switch components.

You can simply pass a Phoenix form field to the components

  • In controller views, the client is the source of truth.
  • In LiveView, the server remains the source of truth and components sync accordingly
  • Validation errors stay fully synchronized and are fully customizable.
  • Error rendering follows Phoenix’s native input interaction model, preventing error messages from appearing on fields the user has not interacted with.
  • Get error translations with custom Gettext Backend

All components pass Phoenix.LiveViewTest, making them safe drop-in replacements for components generated by Phoenix generators.

The E2E app includes both controller and LiveView form examples generated with:

  • mix phx.gen.html
  • mix phx.gen.live

Read Documentation

https://hexdocs.pm/corex/Corex.Form.html


Phoenix Flash Support

The Toast component now supports the native Phoenix Flash system.

  • Pass flash={@flash} to toast_group and use flash messages as usual.
  • New components are introduced for the following lifecycle events:
    • phx-connected
    • phx-disconnected
    • client-error
    • server-error
  • You can define custom toasts for these four lifecycle events anywhere in your application.

Toast creation is now more flexible, with a loading option available for all toast types.

More details:


Accordion List Support

Thanks to the advise from @achempion, Accordion now supports loading items from a list via Accordion.List.Item.

This feature uses the unified Corex.Collection.Item structure, which includes an additional meta field for attaching custom, dynamic data to each item.

<.accordion
  class="accordion"
  items={[
    %Corex.Accordion.Item{
      value: "lorem",
      trigger: "Lorem ipsum dolor sit amet",
      content: "Consectetur adipiscing elit. Sed sodales ullamcorper tristique.",
      meta: %{
        indicator: "hero-chevron-right",
      }
    },
    %Corex.Accordion.Item{
      trigger: "Duis dictum gravida odio ac pharetra?",
      content: "Nullam eget vestibulum ligula, at interdum tellus.",
      meta: %{
        indicator: "hero-chevron-right",
      }
    },
    %Corex.Accordion.Item{
      value: "donec",
      trigger: "Donec condimentum ex mi",
      content: "Congue molestie ipsum gravida a. Sed ac eros luctus.",
      disabled: true,
      meta: %{
        indicator: "hero-chevron-right",
      }
    }
  ]}
>
  <:item :let={item}>
    <.accordion_trigger item={item}>
      {item.meta.trigger}
      <:indicator>
        <.icon name={item.meta.indicator} />
      </:indicator>
    </.accordion_trigger>

    <.accordion_content item={item}>
      {item.meta.content}
    </.accordion_content>
  </:item>
</.accordion>

See more Accordion examples

Select and Combobox Enhancements

Select and Combobox components now support:

  • Grouping
  • Disabling items
  • Per-item customization using dynamic data

These components rely on the unified Corex.Collection.Item structure with a meta field for extensibility.

Corex.Select — Corex v0.1.2
Corex.Combobox — Corex v0.1.2

Design Tokens

Integrate design tokens into Tailwind CSS v4 using Style Dictionary and Tokens Studio, entirely from Elixir, with no Node.js dependency.

Use the mix corex.design mix task to generate and manage design tokens. Full instructions are available in the documentation

netoum

netoum OP

New components

Five new components are available:

  • Clipboard Copy text to the clipboard with configurable trigger and copied states

  • Collapsible Show/hide content with expand/collapse

  • Date Picker Date selection with calendar view and keyboard navigation

  • Dialog Modal dialogs with focus management and accessibility support

  • Tabs Tabbed interfaces for organizing content

Unified list item

Accordion and Tabs now share a common %Corex.List.Item{} struct. Use it for programmatic item lists:

%Corex.List.Item{

  value: "item-1",

  trigger: "Section title",

  content: "Section content",

  meta: %{indicator: "hero-chevron-right"}

}

Form integration

Phoenix Form docs have been expanded for Checkbox, Date Picker, Select, and Switch, including:

  • Controller usage

  • LiveView usage with controlled mode

  • Ecto changeset examples

See each component’s documentation for examples.

Feedback is always welcome. Happy coding

netoum

netoum OP

Dark Mode Toggle

Built on Corex.ToggleGroup, it uses a triple-layer approach (cookies + localStorage + immediate script execution) to ensure:

  • No FOUC (Flash of Unstyled Content)
  • Syncs across browser tabs
  • Respects system preferences
  • Works perfectly with LiveView and controllers

Signature Pad

  • Full Phoenix form integration (controllers & LiveView)
  • Works with and without Ecto changesets
  • Controlled/uncontrolled modes
  • Customizable drawing options (color, size, pressure simulation)

Signature Pad joins the growing collection of form components (checkbox, select, date picker) that work seamlessly in both traditional controllers and LiveView, with or without Ecto Changeset

Happy coding

netoum

netoum OP

Corex Demo is live

Thanks to Gigalixir to offer a great hosting experience for elixir and Phoenix
Try Corex in production with some example for each components, a Phoenix form in a controller and in a Live View, language switch, dark mode and site navigation

Language Switch and RTL

Using the select component, Plugs and cookies, Corex guide you to implement your own local system using Gettext. RTL support is also available to synchronize all components

https://hexdocs.pm/corex/locale.html

Tree View

New component added to display a tree of data with the ability to use it as a navigation. Works in controller and Live View

Feedback is always welcome. Happy coding

Joep

Joep

Tree view live produces an internal server error on the demo site.

netoum

netoum OP

Thanks for trying out. TreeView Live View page is fixed now.

On the same update I also improved the code splitting and lazy loading of the components.
Only loads the components in use on the current page.
Check out the new “Performance” section on the Installation guide
The chunks we see are the shared parts between the components, so no duplication.

Where Next? Top

Trending in Announcing Top

wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
fuelen
Hi all! I want to present a small library which provides a mix task for generating an Entity-Relationship Diagram for Ecto schemas. You...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
AstonJ
This showed up on my feed.. anyone heard of it? Just hype? Ox Alpha is a reasoning model designed for coding, sustained ag...
New
sergio
It’s not that it’s vocabulary is too advanced. It’s something worse. I get lost trying to follow even a paragraph written by Claude. It’...
New
sorenone
Today we’re releasing Oban for Python. Not an Oban client in Python. Not a pythonx wrapper embedded in Elixir. Nope, it’s a fully operati...
New
akoutmos
@hugobarauna, Dr. Dimitrios Koutmos (my brother) and I (Alex Koutmos) have been hard at work on writing a book on how you can use Elixir ...
New
pferriby
Introductory paragraph I’ll be looking for a keen junior or someone that has a couple of years experience in the real world (so you’ve be...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews