@arnelirobles/express-rnx
v1.0.0
Published
Express middleware for rnxJS reactive components integration
Downloads
100
Maintainers
Readme
@arnelirobles/express-rnx
Express middleware and view helpers for integrating rnxJS reactive components into Express applications.
Overview
express-rnx provides Express middleware that makes it easy to:
- Include rnxJS library and stylesheets in your views
- Create reactive state from server data
- Render rnxJS components with template variables
- Initialize rnxJS plugins (router, toast, storage)
- Bind server data to reactive components
Installation
npm
npm install @arnelirobles/express-rnxyarn
yarn add @arnelirobles/express-rnxQuick Start
1. Setup Express Middleware
const express = require('express');
const rnxMiddleware = require('@arnelirobles/express-rnx');
const app = express();
// Add rnx middleware
app.use(rnxMiddleware({
cdn: true,
theme: 'bootstrap'
}));
// Set view engine
app.set('view engine', 'ejs');
app.set('views', './views');
// Routes
app.get('/', (req, res) => {
res.render('index');
});
app.listen(3000);2. Use in EJS Templates
<head>
<%- rnx.scripts() %>
</head>
<body>
<%
const userData = {
name: 'John Doe',
email: '[email protected]'
};
%>
<%- rnx.state(userData, 'user') %>
<p>Welcome, <span data-bind="user.name"></span>!</p>
<%- rnx.component('Button', { variant: 'primary', label: 'Save' }) %>
</body>3. Available Methods
// In your Express route handlers
app.get('/profile', (req, res) => {
const user = req.user; // Your user object
res.render('profile', {
user: user,
// res.locals.rnx helpers are automatically available
});
});Middleware Configuration
Configure the middleware when initializing:
app.use(rnxMiddleware({
// Include rnxJS scripts (default: true)
cdn: true,
// Theme variant (default: 'bootstrap')
// Options: 'bootstrap', 'm3', 'plugins', or null
theme: 'bootstrap',
// Storage configuration
storagePrefix: 'myapp_',
// Router configuration
routerMode: 'hash',
// Toast configuration
toastPosition: 'top-right',
toastDuration: 3000,
toastMax: 5
}));View Helpers Reference
rnx.scripts()
Include rnxJS library and stylesheets.
Syntax:
rnx.scripts(cdn, theme)Parameters:
cdn(boolean, default: true) - Use CDN for resourcestheme(string, default: 'bootstrap') - Theme variant
Example:
<head>
<%- rnx.scripts(true, 'm3') %>
</head>rnx.state()
Create reactive state from server data.
Syntax:
rnx.state(data, varName)Parameters:
data- JavaScript object or array to serializevarName(string, default: 'state') - Global state variable name
Example:
<%
const appData = {
user: {
name: req.user.name,
email: req.user.email,
role: req.user.role
},
notifications: notifications,
routes: {
home: '/',
profile: '/profile',
settings: '/settings'
}
};
%>
<%- rnx.state(appData, 'appState') %>
<span data-bind="appState.user.name"></span>
<span data-bind="appState.user.email"></span>rnx.component()
Render rnxJS components.
Syntax:
rnx.component(name, props)Parameters:
name(string) - Component nameprops(object, optional) - Component properties
Example:
<%- rnx.component('Button', { variant: 'primary', label: 'Save' }) %>
<%- rnx.component('Input', {
type: 'email',
placeholder: '[email protected]',
data_bind: 'state.email'
}) %>
<%- rnx.component('DataTable', {
data: 'state.users',
columns: 'state.columns',
sortable: true,
pageable: true
}) %>rnx.plugin()
Initialize rnxJS plugins.
Syntax:
rnx.plugin(name, options)Parameters:
name(string) - Plugin name ('router', 'toast', 'storage')options(object, optional) - Plugin configuration
Example:
<%- rnx.plugin('router', { mode: 'hash', default_route: '/' }) %>
<%- rnx.plugin('toast', {
position: 'top-right',
duration: 3000,
max_toasts: 5
}) %>
<%- rnx.plugin('storage', {
prefix: 'myapp_',
storage: 'localStorage'
}) %>rnx.dataBind()
Create a data-bind attribute.
Syntax:
rnx.dataBind(path)Example:
<span <%- rnx.dataBind('user.name') %>></span>
<!-- Output: <span data-bind="user.name"></span> -->rnx.dataRule()
Create a validation rule attribute.
Syntax:
rnx.dataRule(rules)Example:
<input <%- rnx.dataRule('required|email') %> />
<!-- Output: <input data-rule="required|email" /> -->
<input <%- rnx.dataRule(['required', 'email', 'max:100']) %> />Complete Example
app.js
const express = require('express');
const rnxMiddleware = require('@arnelirobles/express-rnx');
const app = express();
// Middleware
app.use(express.static('public'));
app.use(rnxMiddleware({
cdn: true,
theme: 'bootstrap',
toastPosition: 'top-right',
toastDuration: 3000
}));
app.set('view engine', 'ejs');
app.set('views', './views');
// Mock user
const mockUser = {
id: 1,
name: 'John Doe',
email: '[email protected]',
role: 'admin'
};
const mockNotifications = [
{ id: 1, title: 'Welcome', message: 'Welcome to the app!', type: 'info' },
{ id: 2, title: 'Success', message: 'Your profile was updated', type: 'success' }
];
// Routes
app.get('/', (req, res) => {
res.render('index', {
user: mockUser,
notifications: mockNotifications
});
});
app.get('/profile', (req, res) => {
res.render('profile', {
user: mockUser
});
});
app.get('/components', (req, res) => {
const users = [
{ id: 1, name: 'Alice', email: '[email protected]', status: 'active' },
{ id: 2, name: 'Bob', email: '[email protected]', status: 'inactive' },
{ id: 3, name: 'Charlie', email: '[email protected]', status: 'active' }
];
const columns = [
{ key: 'id', label: 'ID' },
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'status', label: 'Status' }
];
res.render('components', {
users: users,
columns: columns
});
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});views/layout.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rnxJS Express Example</title>
<%- rnx.scripts(true, 'bootstrap') %>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">My App</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/profile">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/components">Components</a>
</li>
</ul>
</div>
</div>
</nav>
<main class="container mt-5">
<%- body %>
</main>
<%- rnx.plugin('toast', { position: 'top-right' }) %>
</body>
</html>views/index.ejs
<%
const appState = {
user: user,
notifications: notifications,
page: 'home'
};
%>
<%- rnx.state(appState, 'appState') %>
<h1>Welcome to rnxJS!</h1>
<p>Hello, <span data-bind="appState.user.name"></span>!</p>
<div class="row mt-5">
<div class="col-md-6">
<h2>Recent Notifications</h2>
<% notifications.forEach(notif => { %>
<%- rnx.component('Card', {
title: notif.title,
content: notif.message,
type: notif.type
}) %>
<% }); %>
</div>
<div class="col-md-6">
<h2>Actions</h2>
<%- rnx.component('Button', {
variant: 'primary',
label: 'View Profile',
onclick: 'window.location.href = "/profile"'
}) %>
<br><br>
<%- rnx.component('Button', {
variant: 'secondary',
label: 'View Components',
onclick: 'window.location.href = "/components"'
}) %>
</div>
</div>views/profile.ejs
<%
const profileState = {
user: user
};
%>
<%- rnx.state(profileState, 'profileState') %>
<%- rnx.plugin('toast', { position: 'top-right' }) %>
<h1>User Profile</h1>
<div class="profile-card">
<h2 data-bind="profileState.user.name"></h2>
<p><strong>Email:</strong> <span data-bind="profileState.user.email"></span></p>
<p><strong>Role:</strong> <span data-bind="profileState.user.role"></span></p>
<%- rnx.component('Button', {
variant: 'primary',
label: 'Edit Profile',
onclick: 'window.rnx.toast.info("Opening edit dialog")'
}) %>
<%- rnx.component('Button', {
variant: 'danger',
label: 'Logout',
onclick: 'window.rnx.toast.warning("Logging out...")'
}) %>
</div>Security Considerations
HTML Escaping
All component props and attributes are automatically HTML-escaped to prevent XSS:
<%- rnx.component('Button', { label: userInput }) %>
<!-- userInput is escaped automatically -->Data Binding Expressions
String values starting with state., {, or [ are preserved for data binding:
<%- rnx.component('Div', { content: 'state.message' }) %>
<!-- Preserves as: content="state.message" -->
<%- rnx.component('Div', { title: 'Hello' }) %>
<!-- Escapes and quotes: title="Hello" -->Testing
Tests are included. Run with:
npm testContributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
MPL-2.0 - See LICENSE for details.
Support
- Documentation: rnxJS Documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Changelog
1.0.0 (2024)
- Initial release
- Express middleware with res.locals.rnx helpers
- View helpers: scripts, state, component, plugin
- Attribute helpers: dataBind, dataRule
- Bootstrap 5.3+ support
- Plugin integration (router, toast, storage)
- Full XSS prevention and HTML escaping
- Support for EJS, Pug, and other template engines
- Configuration options
