@apexcura/ui-builder
v1.8.9
Published
A low-code UI builder library for dynamic form generation and reusable components built with React, Redux, Tailwind, and Ant Design.
Readme
@apexcura/ui-builder
A powerful, JSON Schema-driven UI renderer built for React applications. It handles everything — from rendering UI elements to managing state via Redux, making API calls, handling validations, and defining event flows — all configurable through a single JSON schema.
⚡ Declarative. Scalable. Developer-friendly.
✨ Features
🧹 Schema-Driven UI Rendering Define forms, tables, dashboards, charts, and dynamic layouts using a flexible JSON structure.
🧠 State Management with Redux Toolkit Automatically sets up Redux stores, reducers, actions, and selectors for your UI based on the schema.
🔁 Built-in API Integration Perform GET, POST, PUT, DELETE requests — all defined declaratively in the schema.
⚙️ Event Handling Without Code Manage click handlers, form submissions, conditional rendering, and navigation using event maps.
💿 Redux Persist Support Automatically persists state across sessions.
🎨 TailwindCSS + Ant Design UI Combines the flexibility of Tailwind with the power of Ant Design components.
📊 Highcharts Support Render charts using
highchartsandhighcharts-react-officialdirectly from JSON.🔐 Form Validations, Modals, Toasts, Print View, Rich Text, and More All natively supported via schema keys.
🚀 Get Started
Installation
npm install @apexcura/ui-builder⚠️ Make sure to install the required peer dependencies:
npm install react react-dom react-redux @reduxjs/toolkit redux-thunk redux-persist react-router-domℹ️ Add required CSS imports to your main application file:
import '@apexcura/ui-builder/dist/styles/index.css';🛠 Usage
Inside the project's reducers list add dynamicStateReducer also into it with name dynamic
import { combineReducers } from "@reduxjs/toolkit";
import { resetState } from "./actions";
import { dynamicStateReducer } from "@apexcura/ui-builder"; // Package Slice
import appStateReducer from "./appState/appStateSlice"; // Project slice
const appReducer = combineReducers({
appState: appStateReducer,
dynamic: dynamicStateReducer, // UIBuilder's state slice
});
const rootReducer = (state: RootState | undefined, action: any): RootState => {
if (action.type === resetState.type) {
state = undefined;
}
return appReducer(state, action);
};
export default rootReducer;
export type RootState = ReturnType<typeof appReducer>;Create a wrapper component that provides the necessary context:
// src/index.tsx
function App() {
return (
<ACWrapperContext
props={{
constants: CONSTANTS, // constants if you have any
store: store, // redux store object
utils: Utils, // Utils object of that project (which contains makeApiCall function)
}}
>
<Provider store={store}>
...
</Provider>
</ACWrapperContext>
);
}ℹ️ As each project has its own api calling patterns along with their own base urls, our library directly call the project's api functions using project's base urls only
Creating Your First View
Define a schema and use the UIBuilder component to render it:
import { UIBuilder } from "@apexcura/ui-builder";
import useAppNavigate from "../../hooks/useAppNavigate";
import basicSchema from "../../schemas/basicSchema.json";
const BasicExample = () => {
const navigate = useAppNavigate(); // you can use useNavigate hook also
return <UIBuilder json={basicSchema} navigate={navigate} />;
};
export default BasicExample;📄 Example Schema
{
"name": "basic-example-form",
"schema": [
{
"name": "physio_evaluation",
"initial_states": {
"org_name": "@localStorage.organization.name"
},
"className": "w-full flex bg-white rounded-md p-2 py-6",
"fields": [
{
"name": "name",
"label": "First Name",
"element": "input-text",
"required": true,
"placeholder": "Eg: Jon Doe",
"color": "primary",
"valueSource": {
"storedLocation": "patient_details.first_name"
},
"handlers": [
{
"action": "changeState",
"type": "onChange",
"args": {
"storedLocation": "patient_details.first_name"
}
}
]
},
{
"name": "phno",
"label": "Phone number",
"element": "input-number",
"required": true,
"placeholder": "Eg: 9123456789",
"color": "primary",
"valueSource": {
"storedLocation": "patient_details.phno"
},
"handlers": [
{
"action": "changeState",
"type": "onChange",
"args": {
"storedLocation": "patient_details.phno"
}
}
]
},
{
"name": "btn-cancel",
"label": "Previous",
"element": "button",
"variant": "outlined",
"color": "primary",
"iconClassName": "aci-left-arrow aci-dynamic-size size-[12px]",
"handlers": [
{
"action": "changeState",
"args": {
"storedLocation": "selectedTab",
"value": "patient_details"
}
}
]
},
{
"name": "btn-submit",
"label": "Save and next",
"iconClassName": "aci-right-arrow aci-dynamic-size size-[12px]",
"iconPosition": "right",
"element": "button",
"variant": "solid",
"handlers": [
{
"action": "validateForm",
"args": {
"onTrue": [
{
"action": "makeApiCall",
"args": {
"api": {
"endpoint": "/addPatient",
"method": "POST",
"payload": {
"patient_umr": "@state.patient_details.uhid",
"formData": "@state.patient_details"
},
"onSuccess": [
{
"action": "displayToast",
"args": {
"type": "success",
"message": "Patient added successfully!!!"
}
}
]
}
}
}
]
}
}
]
},
{
"name": "effects",
"handlers": [
{
"action": "makeApiCall",
"args": {
"api": {
"endpoint": "/patient/${patient_id}",
"method": "GET",
"params": {
"patient_id": "@state.patient_id"
},
"onSuccess": [
{
"action": "changeState",
"args": {
"storedLocation": "patient_details_resp"
}
}
],
"onFailure": [
{
"action": "displayToast",
"args": {
"type": "error",
"message": "Unable to fetch details"
}
}
]
}
},
"dependencies": ["patient_id"]
}
]
}
],
"handlers": []
}
]
}🧪 Development
Build the project:
npm install
npm run buildWatch for changes during development:
npm run watchLint and format code:
npm run lint
npm run format🧹 Built With
- ⚛️ React 18
- 🧰 Redux Toolkit
- 💿 Redux Persist
- 🧠 Redux Thunk
- 🎨 TailwindCSS + Ant Design
- 📊 Highcharts
- 📄 React Quill
- 🖰 React To Print
- 📦 Webpack
📘 License
ISC © [Apex Cura HealthCare]
