Aurora UIX - an opinionated UI generator

Finally, after several months of work, 480+ commits, and nearly 190 PRs, I’m proud to announce the first release of Aurora UIX:

Aurora UIX is a highly opinionated UI generator for Ecto schemas — very simple to use (even if not exactly a simple library).

As expected for a first release, there are still features to add, and some existing parts will certainly need refinement. I believe it is now ready for the community to explore and experiment with.

I invite you to check out the documentation:

All feedback, suggestions, and contributions are very welcome!

8 Likes

Looking great , will check it during this weekend. The declarative design maybe would be great if it work with Ash too.

1 Like

Thanks.

I’ll try to get acquainted with Ash, and evaluate that possibility.

Appreciate your comment!

5 Likes

Aurora UIX v0.1.1 is now available! :tada:

This version refactors show views to use LiveComponents instead of standalone LiveView modules, laying the foundation for record navigation in modals.

Key Changes
- Show views now render as ShowComponent within Index LiveView
- Routes consolidated: /:id/show and /:id/show-edit

See the release note:

Aurora UIX v0.1.2 is now available! :tada:

This version introduces record navigation, allowing users to move between records directly from show and edit views without returning to the index.

Key Changes

  • Next/Previous navigation buttons in show/edit views
  • Option to disable navigation when needed

See the release notes

Links: Hex | Docs | GitHub

2 Likes

About Integrating with Ash

I’ve recently started getting acquainted with the Ash framework:

Since I’ve only scratched the surface so far, it’s very possible that there are better integration approaches than the one I’m currently taking. I’d really appreciate feedback from those with more experience with Ash.

Aurora UIX is currently tested to work well with Ecto schemas and Phoenix forms. Schema attributes are enhanced with UI-specific metadata that is not available in standard Ecto field definitions.

Sorting, filtering, and pagination are supported via options that can be updated dynamically through event logic.

Field / Attribute Differences

Aurora UIX relies on primitive and built-in Ecto field types to derive the initial UI representation of a field, and then provides mechanisms to further refine and enhance the UI characteristics of each field.

Ash’s attribute definitions, on the other hand, are extremely rich. Many of their options are well-suited to providing UI guidance (validation, visibility, constraints, etc.), and Ash also offers a broader range of types than Ecto.

This raises an important question: should I write a custom extension?

For now, I’ve decided not to. My goal is to preserve a clear separation between the model and the view. The idea is that a parser can extract most of the relevant UI-related characteristics from Ash resources, while still allowing overrides or refinements in a separate view module.

That said, if I’m overlooking important benefits or best practices here, I’d very much welcome your comments.

To support this approach, I’m working on a parser capable of reading Ash resources and translating their metadata into Aurora UIX’s existing UI definitions.

CRUD Operations

Aurora UIX currently relies on context functions—or manually defined functions—for:

  • Reading (full list, paginated list, single record)
  • Creating
  • Updating
  • Deleting

It expects the Aurora CTX pagination type and Ecto-like query options.

Ash, however, uses actions, which are powerful and self-contained :slightly_smiling_face:. They can handle filtering, sorting, pagination, authorization, and more.

This leads to a critical crossroads, and I currently see two possible approaches:

  1. Write a parser that converts Ash actions into a set compatible with the existing Aurora UIX implementation.
  2. Refactor Aurora UIX to work directly with Ash actions and adapt pagination to use Ash’s native implementation when actions are involved.

Option 1 is the simplest, but I’m concerned it would limit or dilute a lot of the functionality Ash already provides.

Option 2 is more complex, but it would immediately unlock fine-grained control over what is shown or editable based on authorization policies and action definitions.

I’m very open to suggestions or insights on this topic.

1 Like

If you aren’t already familiar, Ash has a lot of introspection tooling: Ash.Resource.Info — ash v3.13.1 I’m not sure what you meant by “parsing,” but almost any data you could want about an Ash resource is readily available via the Info API.

Regarding an extension, I would advise strongly against building a resource extension for UI (I’ve learned this the hard way by doing that very thing). There are a number of issues, first being that it really harms compile time and module dependencies, bloats the resource files, and also the UI for a given resource may be presented multiple ways, so it’s better to decouple the UI from the backend.

That said, Ash’s DSL is written using Spark, which is a great way to make your own DSL. What I would recommend is writing a Spark DSL for the UI config. The flow would be something like:

  1. Use introspection to generate the initial DSL
  2. Use validators (Spark) in the DSL that check against the Ash resources
  3. Use transformers (Spark) to output a data format the UI components know how to render, or directly generate components (whatever fits your lib’s vision)
3 Likes

Thanks @frankdugan3, I appreciate your feedback.

As you pointed out, I don’t intend to couple the backend with the UI, so my initial approach is aligned with that goal. I’m relying on introspection to extract what I need from Ash, so yes—the “parsing” I mentioned is exactly that.

I wasn’t aware that extensions could have such a significant impact on compilation time, so thanks for highlighting that; it’s an important consideration.

Although Aurora.Uix already has its own DSL, I’ll consider replacing it with Spark. It does seem like it could be a meaningful improvement to the developer experience.

My remaining concern is around data interaction (CRUD). The current implementation operates on function references for reading, updating, etc., with the library handling the arguments based on the expected behavior. I can achieve something similar with Ash by encapsulating calls to actions, but I’m concerned this might significantly undermine some of Ash’s strengths.

That said, I’ll experiment with using Ash actions directly rather than trying to adapt them to my existing implementation and see how far I can get with that approach.

2 Likes

I should clarify, it’s not so much that extensions add a lot of compile time in general (most are pretty light), it’s more that it creates hard dependencies widely across the front and back, which often means hefty recompilation. For example, if I want to tweak something in the user show page, if that was an extension in the resource itself, it would trigger recompiling all the other resources with hard deps on the user module, the router, etc. Much nicer to edit the DSL for the page it self, and have that be the only thing that recompiles!

Regarding CRUD, Ash does have a strong concept of the action types, which do map to CRUD on the resource itself. Introspecting the actions for arguments and accepted attributes is definitely OK, and the type of the action provides a consistent API for you to work with. The exception would be generic actions, which can do anything.