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:
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixirconf-us
- #elixir-ls
- #ai
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
achempion
Looking great, congrats on the launch!
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
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 + CSS3solutions (that does not requireJavaScriptat all). See below link for an accordion example:A huge
for making components accessible by default - it’s a huge help for developers!
netoum
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/accordionThank you for the support
netoum
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
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.htmlmix phx.gen.liveRead Documentation
https://hexdocs.pm/corex/Corex.Form.html
Phoenix Flash Support
The Toast component now supports the native Phoenix Flash system.
flash={@flash}totoast_groupand use flash messages as usual.phx-connectedphx-disconnectedclient-errorserver-errorToast creation is now more flexible, with a
loadingoption 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.Itemstructure, which includes an additionalmetafield for attaching custom, dynamic data to each item.See more Accordion examples
Select and Combobox Enhancements
Select and Combobox components now support:
These components rely on the unified
Corex.Collection.Itemstructure with ametafield 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.designmix task to generate and manage design tokens. Full instructions are available in the documentationnetoum
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: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
Dark Mode Toggle
Built on
Corex.ToggleGroup, it uses a triple-layer approach (cookies + localStorage + immediate script execution) to ensure:Signature Pad
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
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
Tree view live produces an internal server error on the demo site.
netoum
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.