JurisJS Examples
Explore live demonstrations of JurisJS features with complete source code. Each example showcases the power of progressive enhancement with the enhance() API.
🔢 Simple Counter
Basic reactive state management with increment/decrement functionality using enhance() API
const app = new Juris();
// Enhance the display to show reactive counter value
app.enhance('#counter-display', (props, { getState }) => ({
textContent: () => getState('counter', 0)
}));
// Enhance increment button
app.enhance('#counter-inc', (props, { getState, setState }) => ({
onClick: () => setState('counter', getState('counter', 0) + 1)
}));
// Enhance decrement button
app.enhance('#counter-dec', (props, { getState, setState }) => ({
onClick: () => setState('counter', getState('counter', 0) - 1)
}));
// Enhance reset button
app.enhance('#counter-reset', (props, { setState }) => ({
onClick: () => setState('counter', 0)
}));
<div class="demo-counter">
<div class="count-display" id="counter-display">0</div>
<div>
<button class="demo-button" id="counter-dec">- Decrement</button>
<button class="demo-button" id="counter-inc">+ Increment</button>
</div>
<div>
<button class="demo-button secondary" id="counter-reset">Reset</button>
</div>
</div>
📝 Todo List
Dynamic list management with add, toggle, and delete operations using pure enhance() API
const app = new Juris({
states: {
todos: [
{ id: 1, text: 'Learn JurisJS', completed: false },
{ id: 2, text: 'Build amazing apps', completed: false }
]
}
});
// Enhance todo checkboxes - Pure enhance() API!
app.enhance('.todo-checkbox', (props, { getState, setState }) => ({
onClick: () => {
const todoId = parseInt(props.element.dataset.id);
const todos = getState('todos', []);
const updated = todos.map(todo =>
todo.id === todoId ? { ...todo, completed: !todo.completed } : todo
);
setState('todos', updated);
},
className: () => {
const todoId = parseInt(props.element.dataset.id);
const todos = getState('todos', []);
const todo = todos.find(t => t.id === todoId);
return `todo-checkbox ${todo?.completed ? 'checked' : ''}`;
}
}));
// Enhance delete buttons - Clean and simple!
app.enhance('.todo-delete', (props, { getState, setState }) => ({
onClick: () => {
const todoId = parseInt(props.element.dataset.id);
const todos = getState('todos', []);
setState('todos', todos.filter(todo => todo.id !== todoId));
}
}));
// Enhance todo items for completion styling
app.enhance('.todo-item', (props, { getState }) => ({
className: () => {
const todoId = parseInt(props.element.dataset.id);
const todos = getState('todos', []);
const todo = todos.find(t => t.id === todoId);
return `todo-item ${todo?.completed ? 'completed' : ''}`;
}
}));
// Add new todos on Enter
app.enhance('#todo-input', (props, { getState, setState }) => ({
onKeyPress: (e) => {
if (e.key === 'Enter' && e.target.value.trim()) {
const newTodo = {
id: Date.now(),
text: e.target.value.trim(),
completed: false
};
const todos = getState('todos', []);
setState('todos', [...todos, newTodo]);
e.target.value = '';
}
}
}));
<div class="todo-demo">
<input type="text" class="todo-input" id="todo-input" placeholder="Add a new todo..." />
<div class="todo-list" id="todo-list">
<div class="todo-item" data-id="1">
<div class="todo-checkbox" data-id="1"></div>
<span class="todo-text">Learn JurisJS</span>
<button class="todo-delete" data-id="1">Delete</button>
</div>
<div class="todo-item" data-id="2">
<div class="todo-checkbox" data-id="2"></div>
<span class="todo-text">Build amazing apps</span>
<button class="todo-delete" data-id="2">Delete</button>
</div>
</div>
</div>
📋 Reactive Form
Real-time form validation and reactive output display with enhance() API
const app = new Juris({
states: {
form: { name: '', email: '' }
}
});
// Enhance form inputs to update state
app.enhance('#form-name', (props, { setState }) => ({
onInput: (e) => setState('form.name', e.target.value)
}));
app.enhance('#form-email', (props, { setState }) => ({
onInput: (e) => setState('form.email', e.target.value)
}));
// Enhance output to show reactive form data
app.enhance('#form-output', (props, { getState }) => ({
innerHTML: () => {
const name = getState('form.name', '');
const email = getState('form.email', '');
if (!name && !email) {
return 'Fill in the form above to see reactive updates';
}
const isValid = name && email && email.includes('@');
return `
<div>Name: ${name || 'Not provided'}</div>
<div>Email: ${email || 'Not provided'}</div>
<div>Status: ${isValid ? '✅ Valid' : '❌ Invalid'}</div>
`;
}
}));
<div class="form-demo">
<div class="form-group">
<label class="form-label">Name:</label>
<input type="text" class="form-input" id="form-name" />
</div>
<div class="form-group">
<label class="form-label">Email:</label>
<input type="email" class="form-input" id="form-email" />
</div>
<div class="form-output" id="form-output">
Fill in the form above to see reactive updates
</div>
</div>
🎨 Theme Switcher
Dynamic styling based on reactive state changes using enhance() API
Current Theme
Click buttons below to change theme
const app = new Juris({
states: { theme: 'dark' }
});
// Enhance theme demo container
app.enhance('#theme-demo', (props, { getState }) => ({
className: () => `theme-demo ${getState('theme', 'dark')}`
}));
// Enhance theme display text
app.enhance('#theme-display', (props, { getState }) => ({
textContent: () => `Active theme: ${getState('theme', 'dark')}`
}));
// Enhance theme buttons
app.enhance('#theme-light', (props, { setState }) => ({
onClick: () => setState('theme', 'light')
}));
app.enhance('#theme-dark', (props, { setState }) => ({
onClick: () => setState('theme', 'dark')
}));
app.enhance('#theme-auto', (props, { setState }) => ({
onClick: () => setState('theme', 'auto')
}));
.theme-demo {
padding: 2rem;
border-radius: 12px;
transition: all 0.3s ease;
}
.theme-demo.light {
background: #f8fafc;
color: #1e293b;
}
.theme-demo.dark {
background: #1e293b;
color: #e2e8f0;
}
.theme-demo.auto {
background: linear-gradient(45deg, #f8fafc 50%, #1e293b 50%);
color: #7c3aed;
}
🛣️ Simple Router
Client-side routing with dynamic page content using enhance() API
🏠 Home Page
Welcome to the home page! Click the navigation buttons above to see different pages.
const app = new Juris({
states: { currentRoute: 'home' }
});
const routePages = {
'home': { icon: '🏠', title: 'Home Page', content: 'Welcome to the home page!' },
'about': { icon: '📋', title: 'About Page', content: 'Learn more about our application.' },
'contact': { icon: '📞', title: 'Contact Page', content: 'Get in touch with us!' },
'404': { icon: '❌', title: '404 Not Found', content: 'Page not found.' }
};
// Enhance router content to show current page
app.enhance('#router-content', (props, { getState }) => ({
innerHTML: () => {
const route = getState('currentRoute', 'home');
const page = routePages[route] || routePages['404'];
return `
<h4>${page.icon} ${page.title}</h4>
<p>${page.content}</p>
`;
}
}));
// Enhance route buttons
app.enhance('#route-home', (props, { setState }) => ({
onClick: () => setState('currentRoute', 'home')
}));
app.enhance('#route-about', (props, { setState }) => ({
onClick: () => setState('currentRoute', 'about')
}));
app.enhance('#route-contact', (props, { setState }) => ({
onClick: () => setState('currentRoute', 'contact')
}));
app.enhance('#route-404', (props, { setState }) => ({
onClick: () => setState('currentRoute', '404')
}));
// Enhance buttons to show active state
app.enhance('.router-nav button', (props, { getState }) => ({
className: () => {
const currentRoute = getState('currentRoute', 'home');
const buttonRoute = props.element.id.replace('route-', '');
return buttonRoute === currentRoute ? 'active' : '';
}
}));
// Advanced router configuration
const app = new Juris({
router: {
routes: {
'/': 'HomePage',
'/user/:id': {
component: 'UserPage',
guards: ['authGuard'],
loadData: 'loadUserData'
},
'/admin': {
component: 'AdminPage',
guards: ['authGuard', 'adminGuard']
}
},
guards: {
authGuard: ({ getState, navigate }) => {
if (!getState('user.isAuthenticated')) {
navigate('/login');
return false;
}
return true;
},
loadUserData: async ({ params, setState }) => {
const userData = await fetchUser(params.id);
setState('currentUser', userData);
}
}
}
});
🌐 API Integration
Fetch and display data from external APIs with loading states using enhance() API
const app = new Juris({
states: {
apiData: null,
isLoading: false,
error: null
},
services: {
apiService: {
async fetchRandomFact({ setState }) {
setState('isLoading', true);
setState('error', null);
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
const facts = [
"JurisJS is 8x faster than most popular frameworks",
"Progressive enhancement allows existing HTML to become reactive",
"JurisJS has zero dependencies and requires no build process"
];
const randomFact = facts[Math.floor(Math.random() * facts.length)];
setState('apiData', { fact: randomFact });
} catch (error) {
setState('error', 'Failed to fetch data');
} finally {
setState('isLoading', false);
}
}
}
}
});
// Enhance fetch button
app.enhance('#fetch-data', (props, { services }) => ({
onClick: () => services.apiService.fetchRandomFact({
setState: app.setState.bind(app)
})
}));
// Enhance API content display
app.enhance('#api-content', (props, { getState }) => ({
innerHTML: () => {
const isLoading = getState('isLoading', false);
const error = getState('error', null);
const data = getState('apiData', null);
if (isLoading) {
return '<div style="color: #ffd700;">⏳ Loading random fact...</div>';
}
if (error) {
return `<div style="color: #ef4444;">❌ ${error}</div>`;
}
if (data) {
return `
<div style="background: rgba(124, 58, 237, 0.1); padding: 1rem; border-radius: 8px; border: 1px solid rgba(124, 58, 237, 0.3);">
<strong>💡 Random Fact:</strong>
<p style="margin-top: 0.5rem; margin-bottom: 0;">${data.fact}</p>
</div>
`;
}
return 'Click button to load a random fact';
}
}));
// Service-based API integration
const app = new Juris({
services: {
userService: {
async getUser(id, { setState }) {
setState('user.loading', true);
try {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
setState('user.data', user);
setState('user.error', null);
} catch (error) {
setState('user.error', error.message);
} finally {
setState('user.loading', false);
}
},
async updateUser(id, updates, { setState, getState }) {
const currentUser = getState('user.data');
setState('user.data', { ...currentUser, ...updates });
try {
await fetch(`/api/users/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updates)
});
} catch (error) {
// Rollback on error
setState('user.data', currentUser);
setState('user.error', 'Update failed');
}
}
}
}
});
Ready to Build with JurisJS?
These examples showcase the power of the enhance() API for progressive enhancement. Static HTML becomes reactive with minimal, declarative code.