rn-dynamic-ui-render
Version:
A dynamic UI rendering engine for React Native
375 lines (307 loc) โข 8.57 kB
Markdown
A powerful, schema-driven UI rendering engine for React Native. Build customizable, market-specific UIs without writing static layouts. Ideal for white-labeled or modular applications.
* **Schema-Based UI**: Define views using JSON instead of JSX for maximum flexibility.
* **Dynamic Rendering**: Supports nested layouts, conditional visibility, dynamic styles, and list rendering.
* **Theming Support**: Centralize colors, fonts, and design tokens for consistent styling.
* **Internationalization**: Built-in support for translations via `react-i18next`.
* **Extensible**: Inject custom logic, actions, helpers, and data context.
* **Safe Evaluation**: Scoped expression evaluation with robust fallback handling.
* **List Rendering**: Seamless integration with `FlatList` for complex, repeatable UIs.
Install the package via npm or yarn:
```bash
npm install rn-dynamic-ui-render
yarn add rn-dynamic-ui-render
```
* `react-native` (>= 0.60)
* `react-i18next` (optional, for translation support)
Install peer dependencies:
```bash
npm install react-native react-i18next
```
1. Install the package and peer dependencies as shown above.
2. Create a JSON schema defining your UI.
3. Pass the schema, theme, handlers, and components to `RNDynamicUIRender`.
4. Optionally, enable translations by setting `translate: true`.
```jsx
import React from "react";
import { View, Text, Button, FlatList } from "react-native";
import RNDynamicUIRender from "rn-dynamic-ui-render";
// Example schema
const schema = [
{
name: "View",
props: { style: { padding: 16 } },
content: [
{
name: "Text",
props: {
textContent: "welcome_title", // Key for translation
style: {
fontSize: "large",
color: "primary",
fontWeight: "bold",
},
},
},
{
name: "Button",
props: {
title: "Click Me",
onPress: "handlePress",
passData: true,
},
},
],
},
];
// Theme configuration
const theme = {
colors: {
primary: "#0066ff",
danger: "#cc0000",
},
font: {
large: 24,
medium: 16,
},
fontWeight: {
bold: "700",
regular: "400",
},
};
// Handlers for logic and data
const handlers = {
handlePress: (item, index) =>
alert(`Pressed! Item: ${JSON.stringify(item)}, Index: ${index}`),
user: { name: "John", age: 25, isLoggedIn: true },
};
export default function App() {
return (
<RNDynamicUIRender
data={schema}
handlers={handlers}
theme={theme}
components={{ View, Text, Button, FlatList }}
translate={true} // Enable translations
/>
);
}
```
> ๐ Note: For translations, configure `react-i18next` as per its documentation. The `textContent` prop (e.g., `welcome_title`) maps to a translation key.
Each node in the schema is a JSON object with the following structure:
```ts
{
name: string; // Component name (e.g., 'Text', 'View')
props?: object; // Props passed to the component
content?: array; // Child nodes to render inside
if?: string | boolean; // Conditional visibility (expression or boolean)
visible?: string | boolean; // Alternative visibility control
visibleExpression?: true; // Use expression-based visibility
listHeaderComponent?: array; // FlatList header
listFooterComponent?: array; // FlatList footer
listEmptyComponent?: array; // FlatList empty state
}
```
```json
{
"name": "Text",
"props": {
"textContent": "Welcome back!",
"if": "{{ user.isLoggedIn }}"
}
}
```
Expressions allow dynamic logic in schemas using JavaScript-like syntax, safely evaluated in a scoped context.
* Variables: `user.name`
* Ternary operators: `{{ user.age > 18 ? 'Adult' : 'Minor' }}`
* List item access: `item.price` (inside FlatList)
```json
{
"name": "Text",
"props": {
"textContent": "Welcome back!",
"if": "{{ user.isLoggedIn }}"
}
}
```
```json
{
"name": "Text",
"props": {
"style": {
"fontSize": {
"expression": "{{ user.age > 18 ? 22 : 16 }}"
}
}
}
}
```
```json
{
"name": "FlatList",
"props": {
"data": "userList",
"keyExtractor": "id"
},
"content": [
{
"name": "Text",
"props": {
"textContent": "item.name"
}
}
],
"listHeaderComponent": [
{ "name": "Text", "props": { "textContent": "User List" } }
],
"listEmptyComponent": [
{ "name": "Text", "props": { "textContent": "No users found" } }
]
}
```
> โน๏ธ `keyExtractor` can be a string or function. `$item$` and `$index$` are auto-injected in list rows.
```ts
const theme = {
colors: {
primary: "#0066ff",
text: "#333",
},
font: {
small: 14,
large: 24,
},
fontWeight: {
regular: "400",
bold: "700",
},
};
```
```json
{
"name": "Text",
"props": {
"style": {
"color": "primary",
"fontSize": "large",
"fontWeight": "bold"
}
}
}
```
```jsx
<RNDynamicUIRender
handlers={{
user: { name: "Alex", isLoggedIn: true },
userList: [
{ id: "1", name: "Alice" },
{ id: "2", name: "Bob" },
],
handleClick: (item, index) =>
alert(`Clicked: ${item?.name}, Index: ${index}`),
}}
/>
```
> Props like `onPress` and `onChange` resolve to handler keys. Use `passData: true` to inject list item & index.
| Prop | Type | Description | Default |
| ------------ | ------- | ------------------------------------- | ------- |
| `data` | array | JSON schema defining the UI | - |
| `handlers` | object | Data and logic context | `{}` |
| `components` | object | Custom component registry | `{}` |
| `theme` | object | Design tokens for styling | `{}` |
| `translate` | boolean | Enable translations via react-i18next | `false` |
```tsx
import RNDynamicUIRender, { SchemaNode } from "rn-dynamic-ui-render";
const schema: SchemaNode[] = [
{
name: "Text",
props: { textContent: "Hello, TypeScript!" },
},
];
<RNDynamicUIRender data={schema} translate={true} />;
```
Covers types for:
* `RNDynamicUIRender` component
* SchemaNode format
* Handlers, theme, props
```json
[
{
"name": "View",
"content": [
{
"name": "Text",
"props": {
"textContent": "{{ user.isLoggedIn ? 'Welcome back!' : 'Please log in' }}"
}
}
]
}
]
```
```json
{
"name": "FlatList",
"props": {
"data": "userList",
"keyExtractor": "id"
},
"content": [
{
"name": "View",
"props": {
"style": {
"padding": 8
}
},
"content": [
{
"name": "Text",
"props": {
"textContent": "item.name"
}
}
]
}
]
}
```
Contributions are welcome! Please follow these steps:
1. Fork the repository.
2. Create a feature branch: `git checkout -b feature/your-feature`
3. Commit your changes: `git commit -m 'Add feature'`
4. Push to the branch: `git push origin feature/your-feature`
5. Open a pull request.
See `CONTRIBUTING.md` for details.
This project is licensed under the MIT License. See the `LICENSE` file for details.
**Q: Why are my translations not working?**
**A:** Ensure `react-i18next` is configured and `translate: true` is set in `RNDynamicUIRender`.
**Q: How do I debug schema errors?**
**A:** Check the console for errors. Ensure `name` matches a registered component and expressions are valid.
**Q: Can I use custom components?**
**A:** Yes, pass them in the `components` prop, e.g., `{ MyComponent }`.
---