Complete Guide & Migration Documentation
The RSL JSON Layout Engine v2 is a complete evolution of the v1 API, bringing powerful new capabilities while maintaining 100% backward compatibility with existing v1 configurations.
All existing v1 configurations will continue to work without any changes. You can upgrade to v2 immediately without breaking anything!
Semantic components (card, button, badge, alert, etc.) PLUS interactive wrappers (select, modal, tooltip) AND custom HTML for anything else
Render arrays of data with {{interpolation}} - no more repetitive JSON configurations
Hero, features, pricing, testimonials, gallery, stats, CTA, and team layouts in one line of code
Add onClick and custom events directly in your JSON configuration
Inline styles, spacing utilities, alignment, and responsive visibility controls
Build complex layouts with grids inside grids using the new grid content type
The v2 engine automatically detects the version in your configuration and handles it appropriately. All v1 features are fully supported:
version: 1 configurations work exactly as beforehtml, text, slot, componentcolumnStart, columnEnd, rowStart, rowEndrenderMode: "direct" for wrapper-only renderingIf no version is specified, v1 is assumed for backward compatibility. To use v2 features, simply add version: 2 to your configuration.
| Feature | v1 | v2 |
|---|---|---|
| Content Types | 4 types (html, text, slot, component) | 17+ semantic types + custom HTML |
| Template System | ❌ Not available | ✅ Full support with {{interpolation}} |
| Component Presets | ❌ Not available | ✅ 8 ready-to-use presets |
| Event Handlers | ❌ Not available | ✅ onClick and custom events |
| Inline Styles | ❌ Not available | ✅ Full CSS-in-JSON support |
| Auto-generated IDs | ❌ Required | ✅ Optional (auto-generated) |
| Nested Grids | Manual HTML only | ✅ Native grid content type |
v2 introduces 14 powerful content types that cover most UI needs without writing HTML:
Beautiful, flexible card layouts with images, titles, text, buttons, and badges.
{
version: 2,
layoutId: "cards",
breakpoints: { xs: 1, md: 2, lg: 3 },
items: [
{
content: {
type: "card",
image: "image.jpg",
title: "Card Title",
subtitle: "Optional subtitle",
text: "Card description goes here",
badge: { text: "New", variant: "success" },
button: { text: "Learn More", variant: "primary" }
}
}
]
}
default - Standard card with subtle shadowelevated - Card with enhanced shadow and hover effectoutlined - Card with border instead of shadowButtons with multiple variants, sizes, and icons.
content: {
type: "button",
text: "Click Me",
variant: "primary", // primary, secondary, success, danger, warning, info
size: "md", // sm, md, lg
icon: "fa fa-rocket",
onClick: function(event, element) {
alert('Button clicked!');
}
}
Small status indicators and labels.
content: {
type: "badge",
text: "New Feature",
variant: "success" // primary, success, warning, danger, info
}
Notification boxes with icons and optional dismiss button.
content: {
type: "alert",
text: "This is an important message!",
variant: "info", // success, info, warning, danger
icon: "fa fa-info-circle",
dismissible: true
}
Responsive images with captions and aspect ratio control.
content: {
type: "image",
src: "photo.jpg",
alt: "Description",
caption: "Image caption",
aspectRatio: "16/9" // 1/1, 4/3, 16/9, or custom
}
Heading, paragraph, and list components for semantic content.
// Heading
content: {
type: "heading",
level: 2, // 1-6
text: "Section Title"
}
// Paragraph
content: {
type: "paragraph",
text: "Lorem ipsum dolor sit amet..."
}
// List
content: {
type: "list",
items: ["Item 1", "Item 2", "Item 3"],
ordered: false,
icon: "fa fa-check"
}
Data tables with headers and responsive styling.
content: {
type: "table",
headers: ["Name", "Email", "Role"],
rows: [
["John Doe", "john@example.com", "Admin"],
["Jane Smith", "jane@example.com", "User"]
],
striped: true,
hoverable: true
}
Font Awesome icons with size and color control.
content: {
type: "icon",
icon: "fa fa-heart",
size: "3x", // 1x, 2x, 3x, 4x, 5x
color: "#e74c3c"
}
Divider and spacer components for layout control.
// Divider
content: {
type: "divider",
text: "Optional section text"
}
// Spacer
content: {
type: "spacer",
height: "3rem"
}
Styled links with icons.
content: {
type: "link",
text: "Learn More",
href: "/docs",
icon: "fa fa-arrow-right",
external: true // Opens in new tab
}
Create nested grids within items.
content: {
type: "grid",
breakpoints: { xs: 2 },
gap: "1rem",
items: [
{ content: { type: "card", title: "Nested Card 1" } },
{ content: { type: "card", title: "Nested Card 2" } }
]
}
v2 provides semantic wrappers for existing v1 interactive components. These make it much easier to use complex components without writing HTML strings.
Custom select dropdowns with search, multi-select, and more.
content: {
type: "select",
placeholder: "Choose option...",
searchable: true, // Enable search
multi: false, // Multi-select mode
clearable: true, // Show clear button
options: [
{ value: "1", label: "Option 1" },
{ value: "2", label: "Option 2", selected: true },
{ value: "3", label: "Option 3", disabled: true }
],
onChange: function(e, select) {
console.log('Selected:', select.value);
}
}
The select content type wraps the existing v1 Select component, so all v1 features work: search, multi-select, chips, keyboard navigation, etc.
Dialog windows and overlays with flexible content and actions.
content: {
type: "modal",
id: "my-modal",
title: "Modal Title",
size: "default", // small, default, large
showCloseButton: true,
content: "Modal body content goes here", // String or nested content
buttons: [
{
text: "Cancel",
variant: "outline",
classes: ["close-modal"]
},
{
text: "Confirm",
variant: "primary",
onClick: function() {
alert('Confirmed!');
}
}
],
buttonAlign: "right", // left, center, right
show: false // Auto-show on render
}
You can use any v2 content type inside the modal body:
content: {
type: "modal",
id: "form-modal",
title: "User Profile",
content: {
type: "card",
title: "Edit Profile",
text: "Update your information below"
},
buttons: [...]
}
Contextual tooltips with positioning and variants.
content: {
type: "tooltip",
text: "This is helpful information!",
position: "top", // top, bottom, left, right
variant: "default", // default, dark, light
size: "md", // sm, md, lg
trigger: '',
// Or use triggerText for simple text
triggerText: ''
}
You can use any v2 content type as the tooltip trigger:
content: {
type: "tooltip",
text: "Additional information about this button",
position: "top",
trigger: {
type: "button",
text: "Need Help?",
variant: "outline",
icon: "fa fa-question-circle"
}
}
These v2 wrappers give you all the power of v1 components with the clean, semantic syntax of v2. No more writing HTML strings with data attributes!
When you need complete control over markup or want to create designs not covered by the semantic content types, use type: "html". This is not a legacy feature—it's the intentional escape hatch for custom content.
// Custom team member card
content: {
type: "html",
value: `
<div class="team-card">
<img src="photo.jpg" alt="Jane Doe">
<div class="team-info">
<h4>Jane Doe</h4>
<p>Lead Designer</p>
<div class="social-links">
<a href="#"><i class="fab fa-linkedin"></i></a>
<a href="#"><i class="fab fa-twitter"></i></a>
</div>
</div>
</div>
`
}
// Custom stats display
content: {
type: "html",
value: `
<div class="stat-item">
<div class="stat-number">250+</div>
<div class="stat-label">Projects Delivered</div>
</div>
`
}
The real power comes from mixing both approaches. Use semantic types for standard components and custom HTML for unique designs:
RSL.JSONLayout.renderLayout('#page', {
version: 2,
breakpoints: { xs: 1, md: 2, lg: 3 },
items: [
// Semantic: Use built-in heading type
{ content: { type: "heading", level: 2, text: "Our Team" } },
// Custom: Team cards with unique design
{ content: { type: "html", value: `<div class="team-card">...</div>` } },
{ content: { type: "html", value: `<div class="team-card">...</div>` } },
// Semantic: Use built-in button type
{ content: { type: "button", text: "View All", variant: "outline" } }
]
});
Check out the v2 Full Page Template for a complete example combining semantic types with custom HTML to build an entire website page.
In addition to html, these base content types are always available:
| Type | Description | Example |
|---|---|---|
html |
Raw HTML markup | { type: "html", value: "<div>...</div>" } |
text |
Plain text (escaped) | { type: "text", value: "Hello World" } |
slot |
Empty placeholder | { type: "slot" } |
component |
Reference existing DOM element | { type: "component", selector: "#my-widget" } |
The template system eliminates repetitive JSON by allowing you to define a single item template and render it with an array of data.
const products = [
{ name: "Product 1", price: "99", image: "img1.jpg" },
{ name: "Product 2", price: "149", image: "img2.jpg" },
{ name: "Product 3", price: "199", image: "img3.jpg" }
];
const config = {
version: 2,
layoutId: "products",
breakpoints: { xs: 1, md: 2, lg: 3 },
template: {
data: products,
item: {
content: {
type: "card",
image: "{{image}}",
title: "{{name}}",
price: "${{price}}",
button: { text: "Buy Now", variant: "primary" }
}
}
}
};
RSL.JSONLayout.renderLayout('#container', config);
{{key}} - Replace with data[key]{{index}} - Current item index (0-based){{index1}} - Current item index (1-based)Template interpolation works on any string value in the item configuration, including nested properties, classes, and even event handlers!
const teamMembers = [
{
name: "Alex Morgan",
role: "CEO",
bio: "Visionary leader",
avatar: "alex.jpg",
twitter: "#",
linkedin: "#"
},
// ... more members
];
const config = {
version: 2,
layoutId: "team",
breakpoints: { xs: 1, sm: 2, lg: 4 },
template: {
data: teamMembers,
item: {
content: {
type: "card",
image: "{{avatar}}",
title: "{{name}}",
subtitle: "{{role}}",
text: "{{bio}}",
buttons: [
{ text: "Twitter", href: "{{twitter}}", icon: "fab fa-twitter" },
{ text: "LinkedIn", href: "{{linkedin}}", icon: "fab fa-linkedin" }
]
},
style: {
textAlign: "center"
}
}
}
};
Explore interactive Template System examples with live demos: Template System Examples
Presets are pre-configured layouts for common UI patterns. Just provide your data and the preset handles the rest.
Eye-catching hero sections with title, subtitle, image, and CTA buttons.
RSL.JSONLayout.renderPreset('#hero', 'hero', {
id: "hero-section",
title: "Welcome to Our Product",
subtitle: "The best solution for your needs",
image: "hero-bg.jpg",
buttons: [
{ text: "Get Started", variant: "primary", icon: "fa fa-rocket" },
{ text: "Learn More", variant: "secondary" }
]
});
Feature highlights with icons, titles, and descriptions.
RSL.JSONLayout.renderPreset('#features', 'features', {
id: "features-section",
breakpoints: { xs: 1, md: 2, lg: 3 },
features: [
{
icon: "fa fa-bolt",
title: "Lightning Fast",
description: "Optimized for performance"
},
{
icon: "fa fa-shield-alt",
title: "Secure",
description: "Enterprise-grade security"
},
// ... more features
]
});
Pricing tables with plans, features, and CTA buttons.
RSL.JSONLayout.renderPreset('#pricing', 'pricing', {
id: "pricing-section",
breakpoints: { xs: 1, lg: 3 },
plans: [
{
name: "Starter",
price: "9",
period: "month",
description: "Perfect for individuals",
features: ["Feature 1", "Feature 2", "Feature 3"],
button: { text: "Get Started", variant: "primary" }
},
{
name: "Professional",
price: "29",
period: "month",
description: "For growing teams",
features: ["All Starter features", "Feature 4", "Feature 5"],
button: { text: "Get Started", variant: "primary" },
featured: true // Highlights this plan
}
// ... more plans
]
});
Customer testimonials with quotes, avatars, and ratings.
RSL.JSONLayout.renderPreset('#testimonials', 'testimonials', {
id: "testimonials-section",
breakpoints: { xs: 1, md: 2, lg: 3 },
testimonials: [
{
quote: "This product changed everything for us!",
author: "Sarah Johnson",
role: "CEO",
company: "TechCorp",
avatar: "sarah.jpg",
rating: 5
}
// ... more testimonials
]
});
Image galleries with captions.
RSL.JSONLayout.renderPreset('#gallery', 'gallery', {
id: "gallery-section",
breakpoints: { xs: 1, sm: 2, md: 3, lg: 4 },
images: [
{ src: "img1.jpg", caption: "Beautiful landscape" },
{ src: "img2.jpg", caption: "City lights" }
// ... more images
]
});
Key metrics and statistics display.
RSL.JSONLayout.renderPreset('#stats', 'stats', {
id: "stats-section",
breakpoints: { xs: 2, md: 4 },
stats: [
{ value: "10K+", label: "Active Users", icon: "fa fa-users" },
{ value: "99.9%", label: "Uptime", icon: "fa fa-server" }
// ... more stats
]
});
Conversion-focused call-to-action sections.
RSL.JSONLayout.renderPreset('#cta', 'cta', {
id: "cta-section",
title: "Ready to Get Started?",
subtitle: "Join thousands of satisfied customers",
button: { text: "Sign Up Now", variant: "primary", icon: "fa fa-arrow-right" }
});
Team member profiles with photos and social links.
RSL.JSONLayout.renderPreset('#team', 'team', {
id: "team-section",
breakpoints: { xs: 1, sm: 2, lg: 4 },
members: [
{
name: "Alex Morgan",
role: "CEO & Founder",
bio: "Visionary leader with 15 years in tech",
avatar: "alex.jpg",
social: {
twitter: "#",
linkedin: "#",
github: "#"
}
}
// ... more members
]
});
v2 allows you to add interactivity directly in your JSON configuration using event handlers.
content: {
type: "button",
text: "Click Me",
onClick: function(event, element) {
// event: the click event
// element: the button element
alert('Button clicked!');
}
}
content: {
type: "card",
title: "Click Counter",
text: "Clicks: 0",
button: {
text: "Increment",
variant: "primary",
onClick: function(event, element) {
const card = element.closest('.rsl-card');
const textEl = card.querySelector('.rsl-card-text');
const match = textEl.textContent.match(/\d+/);
const count = match ? parseInt(match[0]) + 1 : 1;
textEl.textContent = `Clicks: ${count}`;
}
}
}
Functions cannot be serialized to JSON when storing configurations in a database. For persistent configurations, use custom event names and attach handlers separately after rendering.
// In your config (can be stored in DB)
content: {
type: "button",
text: "Custom Event",
customEvent: "myCustomEvent",
data: { userId: 123 }
}
// After rendering, attach handler
document.addEventListener('myCustomEvent', function(event) {
console.log('Custom event fired!', event.detail);
});
v2 provides multiple ways to control styling beyond just CSS classes.
items: [
{
content: { type: "card", title: "Styled Card" },
style: {
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
color: "white",
padding: "2rem",
borderRadius: "12px"
}
}
]
items: [
{
content: { type: "heading", text: "Title" },
spacing: {
margin: "2rem 0",
padding: "1rem"
}
}
]
items: [
{
content: { type: "card", title: "Centered" },
style: {
textAlign: "center"
}
}
]
items: [
{
content: { type: "card", title: "Desktop Only" },
classes: ["hide-mobile", "show-desktop"]
},
{
content: { type: "card", title: "Mobile Only" },
classes: ["show-mobile", "hide-desktop"]
}
]
Create complex layouts with grids inside grids using the new grid content type.
{
version: 2,
layoutId: "complex-layout",
breakpoints: { xs: 1, lg: 2 },
items: [
{
content: {
type: "card",
title: "Regular Card",
text: "This is a normal card"
}
},
{
content: {
type: "grid",
breakpoints: { xs: 2 },
gap: "1rem",
items: [
{
content: {
type: "card",
title: "Nested 1",
variant: "outlined"
}
},
{
content: {
type: "card",
title: "Nested 2",
variant: "outlined"
}
}
]
}
}
]
}
When dynamically rendering layouts with JavaScript (via buttons, AJAX, etc.), you need to handle responsive column recalculation manually.
The core layout.js file only tracks layouts that exist at DOMContentLoaded. Layouts added dynamically via renderLayout() or renderPreset() won't automatically resize when the viewport changes.
Use a ResizeObserver to watch your container for size changes. This handles window resizes, split-pane adjustments, and any container size changes.
// Setup ResizeObserver for dynamic container
const container = document.getElementById('dynamic-content');
let resizeTimeout;
if (window.ResizeObserver) {
const observer = new ResizeObserver(entries => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
recalculateLayoutColumns();
}, 100); // Debounce for performance
});
observer.observe(container);
}
// Recalculate columns function (mirrors layout.js logic)
function recalculateLayoutColumns() {
const layouts = document.querySelectorAll('.slot-layout');
layouts.forEach(layout => {
const layoutWidth = layout.offsetWidth;
// Get breakpoint values from data attributes
const colsXxl = parseInt(layout.getAttribute('data-cols-xxl')) || null;
const colsXl = parseInt(layout.getAttribute('data-cols-xl')) || null;
const colsLg = parseInt(layout.getAttribute('data-cols-lg')) || null;
const colsMd = parseInt(layout.getAttribute('data-cols-md')) || null;
const colsSm = parseInt(layout.getAttribute('data-cols-sm')) || null;
const colsXs = parseInt(layout.getAttribute('data-cols-xs')) || null;
// Helper to find first defined value
const firstDefined = (...values) =>
values.find(v => v !== null && v !== undefined) ?? null;
// Calculate columns based on width (matches layout.js breakpoints)
let columns;
if (layoutWidth >= 1601) {
columns = firstDefined(colsXxl, colsXl, colsLg, colsMd, colsSm, colsXs, 4);
} else if (layoutWidth >= 1201) {
columns = firstDefined(colsXl, colsLg, colsMd, colsSm, colsXs, 3);
} else if (layoutWidth >= 993) {
columns = firstDefined(colsLg, colsMd, colsSm, colsXs, 3);
} else if (layoutWidth >= 769) {
columns = firstDefined(colsMd, colsSm, colsXs, 2);
} else if (layoutWidth >= 577) {
columns = firstDefined(colsSm, colsXs, 2);
} else {
columns = colsXs !== null ? colsXs : 1;
}
layout.style.setProperty('--dynamic-cols', columns);
});
}
Alternatively, listen to window resize events. This works but won't catch container-specific resizes (like split-pane adjustments).
let resizeTimeout;
window.addEventListener('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
recalculateLayoutColumns();
}, 100);
});
Call recalculateLayoutColumns() after:
// Example: Rendering layout on button click
function loadPreset(presetName) {
const config = presets[presetName];
// Render the layout
RSL.JSONLayout.renderLayout('#container', config);
// Recalculate after a short delay
setTimeout(() => {
recalculateLayoutColumns();
}, 50);
}
// Example: After expanding a container
function expandPreview() {
container.classList.add('fullwidth');
// Wait for CSS transition (300ms) then recalculate
setTimeout(() => {
recalculateLayoutColumns();
}, 350);
}
You don't need to migrate anything! All v1 configurations work in v2. This guide is for those who want to take advantage of new v2 features.
To use v2 features, change version: 1 to version: 2:
// Before (v1)
{
version: 1,
layoutId: "my-layout",
// ...
}
// After (v2)
{
version: 2,
layoutId: "my-layout",
// ...
}
Replace HTML strings with semantic content types:
// Before (v1)
{
id: "card1",
content: {
type: "html",
value: `
Title
Description
`
}
}
// After (v2)
{
content: {
type: "card",
title: "Title",
text: "Description",
button: { text: "Click", variant: "primary" }
}
}
Replace arrays of similar items with templates:
// Before (v1) - Repetitive
{
items: [
{ id: "1", content: { type: "html", value: `Product 1 - $99` } },
{ id: "2", content: { type: "html", value: `Product 2 - $149` } },
{ id: "3", content: { type: "html", value: `Product 3 - $199` } }
]
}
// After (v2) - Template
{
template: {
data: [
{ name: "Product 1", price: "99" },
{ name: "Product 2", price: "149" },
{ name: "Product 3", price: "199" }
],
item: {
content: {
type: "card",
title: "{{name}}",
price: "${{price}}"
}
}
}
}
Replace entire configurations with presets:
// Before (v1) - Manual hero configuration
{
version: 1,
layoutId: "hero",
wrapper: { tag: "section", classes: ["hero"] },
breakpoints: { xs: 1 },
items: [{
id: "hero-content",
content: {
type: "html",
value: `
Welcome
Subtitle
`
}
}]
}
// After (v2) - Preset
RSL.JSONLayout.renderPreset('#hero', 'hero', {
title: "Welcome",
subtitle: "Subtitle",
buttons: [{ text: "Get Started", variant: "primary" }]
});
const products = [
{ id: 1, name: "Wireless Headphones", price: "99", image: "headphones.jpg", rating: "4.5/5" },
{ id: 2, name: "Smart Watch", price: "199", image: "watch.jpg", rating: "4.8/5" },
{ id: 3, name: "Laptop Stand", price: "49", image: "stand.jpg", rating: "4.3/5" }
];
RSL.JSONLayout.renderLayout('#products', {
version: 2,
layoutId: "product-catalog",
breakpoints: { xs: 1, sm: 2, lg: 3 },
gap: "2rem",
template: {
data: products,
item: {
content: {
type: "card",
image: "{{image}}",
title: "{{name}}",
text: "Rating: {{rating}}",
price: "${{price}}",
button: {
text: "Add to Cart",
variant: "primary",
icon: "fa fa-shopping-cart",
onClick: function(event, element) {
const productId = {{id}};
addToCart(productId);
}
}
}
}
}
});
// Hero
RSL.JSONLayout.renderPreset('#hero', 'hero', {
title: "Build Amazing Products",
subtitle: "The complete toolkit for modern development",
image: "hero-bg.jpg",
buttons: [
{ text: "Start Free Trial", variant: "primary" },
{ text: "Watch Demo", variant: "secondary" }
]
});
// Features
RSL.JSONLayout.renderPreset('#features', 'features', {
breakpoints: { xs: 1, md: 2, lg: 3 },
features: [
{ icon: "fa fa-bolt", title: "Fast", description: "Lightning fast performance" },
{ icon: "fa fa-shield-alt", title: "Secure", description: "Enterprise security" },
{ icon: "fa fa-mobile-alt", title: "Responsive", description: "Works everywhere" }
]
});
// Pricing
RSL.JSONLayout.renderPreset('#pricing', 'pricing', {
plans: [
{
name: "Starter",
price: "0",
period: "month",
features: ["5 Projects", "10 GB Storage"],
button: { text: "Get Started", variant: "outline" }
},
{
name: "Pro",
price: "29",
period: "month",
features: ["Unlimited Projects", "100 GB Storage", "Priority Support"],
button: { text: "Get Started", variant: "primary" },
featured: true
}
]
});
// Testimonials
RSL.JSONLayout.renderPreset('#testimonials', 'testimonials', {
testimonials: [
{
quote: "This product is incredible!",
author: "Sarah Johnson",
role: "CEO",
company: "TechCorp",
avatar: "sarah.jpg",
rating: 5
}
]
});
// Stats
RSL.JSONLayout.renderPreset('#stats', 'stats', {
breakpoints: { xs: 2, md: 4 },
stats: [
{ value: "1,234", label: "Total Users", icon: "fa fa-users" },
{ value: "$45K", label: "Revenue", icon: "fa fa-dollar-sign" },
{ value: "98%", label: "Satisfaction", icon: "fa fa-smile" },
{ value: "24/7", label: "Support", icon: "fa fa-headset" }
]
});
// Recent Activity (custom layout with template)
const activities = [
{ user: "John Doe", action: "created a new project", time: "2 mins ago" },
{ user: "Jane Smith", action: "uploaded a file", time: "5 mins ago" }
];
RSL.JSONLayout.renderLayout('#activity', {
version: 2,
layoutId: "activity-feed",
breakpoints: { xs: 1 },
gap: "1rem",
template: {
data: activities,
item: {
content: {
type: "card",
variant: "outlined",
title: "{{user}}",
text: "{{action}}",
meta: "{{time}}"
}
}
}
});
Check out the Template System examples for data-driven rendering with {{interpolation}}, experiment with the live v2 playground to build your own layouts interactively, and browse the component examples for individual component usage.