See how one Filter component can control multiple components in real-time
This demo showcases the FilterBus - RSL's pub/sub system for cross-component communication. The Filter buttons above the table publish their state to the FilterBus, and the Smart Table subscribes to receive those updates.
Click the filter buttons below to filter the table. Try combining filters (e.g., "Active" + "North") to see multi-filter behavior. Use the Date Range picker to filter by date, and the Time Filter to show entries within 2 hours of the selected time. Use the search box for text search across all columns.
| Date | Time | Name | Status | Region | Revenue | |
|---|---|---|---|---|---|---|
| 2025-12-03 | 9:00 AM | John Smith | john.smith@example.com | Active | North | $125,000 |
| 2025-12-02 | 2:30 PM | Sarah Johnson | sarah.j@example.com | Active | South | $98,500 |
| 2025-12-01 | 11:00 AM | Michael Brown | m.brown@example.com | Pending | East | $67,200 |
| 2025-11-30 | 4:45 PM | Emily Davis | emily.d@example.com | Inactive | West | $0 |
| 2025-11-28 | 8:15 AM | Robert Wilson | r.wilson@example.com | Active | North | $215,800 |
| 2025-11-25 | 1:00 PM | Jennifer Lee | j.lee@example.com | Pending | South | $45,000 |
| 2025-11-20 | 10:30 AM | David Martinez | d.martinez@example.com | Active | East | $178,300 |
| 2025-11-15 | 3:15 PM | Lisa Anderson | l.anderson@example.com | Inactive | North | $0 |
| 2025-11-10 | 9:45 AM | James Taylor | j.taylor@example.com | Active | West | $89,600 |
| 2025-11-05 | 5:00 PM | Amanda White | a.white@example.com | Pending | South | $52,100 |
| 2025-11-01 | 12:00 PM | Christopher Harris | c.harris@example.com | Active | East | $143,700 |
| 2025-10-25 | 7:30 AM | Michelle Clark | m.clark@example.com | Inactive | West | $0 |
<!-- Region Filter publishes to FilterBus -->
<div data-rsl-filter data-filter-key="region">
<button data-filter-value="All">All</button>
<button data-filter-value="North">North</button>
<button data-filter-value="South">South</button>
</div>
<!-- Date Picker publishes date range -->
<input type="text"
data-rsl-date-picker
data-mode="range"
data-filterbus-publish="dateFilter"
placeholder="Select date range...">
<!-- Time Picker publishes selected time -->
<input type="text"
data-rsl-time-picker
data-format="12"
data-step="15"
data-show-presets="true"
data-filterbus-publish="timeFilter"
data-placeholder="Select time...">
<!-- Smart Table subscribes to all filters -->
<table data-rsl-smart-table
data-filter-subscribe="region,dateFilter"
data-filter-map="region:region"
data-filterbus-date-field="date">
<thead>
<tr>
<th data-col="date" data-type="date">Date</th>
<th data-col="name">Name</th>
<th data-col="region">Region</th>
<th data-col="revenue" data-type="currency">Revenue</th>
</tr>
</thead>
<tbody>
<tr><td>2025-12-03</td><td>John</td><td>North</td><td>$125,000</td></tr>
</tbody>
</table>
KPI Cards can subscribe to FilterBus for both visibility filtering (show/hide based on region) and date range filtering (recalculate values based on date selection). Try using both the Region buttons and Date Range picker above!
Visibility: Regional cards show/hide based on the Region filter selection (e.g., selecting "North" hides South, East, West).
Date Filtering: ALL cards use data-filterbus-date-field="date" with data-values to recalculate their revenue totals when the date range changes. Try selecting "Last 7 Days" or "Last 30 Days" to see the values update!
<!-- KPI Card with visibility + date filtering -->
<div data-rsl-kpi
data-filter-subscribe="region"
data-filter-value="North"
data-filterbus-date-field="date"
data-aggregate="sum"
data-label="North Revenue"
data-value="$0"
data-prefix="$"
data-icon="fa-map-marker-alt"
data-color-scheme="info">
</div>
<script>
// Set data array for date filtering
var revenueData = [
{ date: "2025-12-01", value: 15000 },
{ date: "2025-12-02", value: 18500 },
{ date: "2025-12-03", value: 12300 }
];
var kpiCard = document.querySelector('[data-rsl-kpi]');
kpiCard.setAttribute('data-values', JSON.stringify(revenueData));
// Re-initialize to pick up data
RSL.KPICards.instances.delete(kpiCard);
RSL.KPICards.create(kpiCard);
</script>
Stats can subscribe to FilterBus for visibility filtering based on region or category. Stats are lighter-weight than KPI Cards, ideal for marketing pages and landing sections.
Visibility: Stats with data-filter-subscribe="region" and data-filter-value="North" will show/hide based on the Region filter selection above.
Use Case: Show different metrics for different regions, departments, or categories. Perfect for dashboards that need to highlight context-specific statistics.
<!-- Stat with FilterBus visibility filtering -->
<div class="rsl-stat"
data-value="847"
data-label="North Customers"
data-icon="fa-map-marker-alt"
data-color-scheme="info"
data-variant="card"
data-animate
data-filter-subscribe="region"
data-filter-value="North">
</div>
<!-- When FilterBus publishes region="North", this stat shows -->
<!-- When FilterBus publishes region="South", this stat hides -->
<!-- When FilterBus publishes region="all", all stats show -->
<script>
// Publish region filter programmatically
RSL.FilterBus.publish('region', 'North');
</script>
Charts can subscribe to FilterBus for both visibility filtering (show/hide based on region) and date range filtering (re-render with filtered data). Try using both the Region buttons and Date Range picker above!
Visibility: Regional charts show/hide based on the Region filter selection (e.g., selecting "North" hides South, East, West charts).
Date Filtering: ALL charts use data-filterbus-date-field="date" with data-time-series-data to re-render with filtered data points when the date range changes. Try "Last 7 Days" vs "Last 30 Days" to see the charts update!
<!-- Chart with visibility + date filtering -->
<div id="revenue-chart"
data-rsl-chart
data-filter-subscribe="region"
data-filter-value="North"
data-filterbus-date-field="date"
data-type="line"
data-title="North Region Trend"
data-color-scheme="info"
data-height="250"
data-curved>
</div>
<script>
// Set time series data for date filtering
var chartData = [
{ date: "2025-12-01", value: 15000 },
{ date: "2025-12-02", value: 18500 },
{ date: "2025-12-03", value: 12300 }
];
var chart = document.getElementById('revenue-chart');
chart.setAttribute('data-time-series-data', JSON.stringify(chartData));
// Re-initialize to pick up data
if (RSL.Charts.destroy) RSL.Charts.destroy(chart);
RSL.Charts.create(chart);
</script>
Gallery items can subscribe to FilterBus for visibility filtering. Click the region buttons above to show/hide region-specific images, or use the date picker to filter by photo date.
Each gallery item has data-filter-subscribe="region" to listen to the Region filter, and data-filter-value="North" (etc.) to specify which filter value it matches. Items without data-filter-value always show.
The gallery also has data-filterbus-date-field="date" to enable date range filtering. Each item has a data-date attribute with its photo date (YYYY-MM-DD format).
<!-- Gallery with date filtering enabled -->
<div class="rsl-gallery"
data-cols="4"
data-filterbus-date-field="date">
<!-- Item with visibility + date filtering -->
<article class="rsl-gallery-item"
data-filter-subscribe="region"
data-filter-value="North"
data-date="2025-12-01">
<div class="rsl-gallery-image">
<img src="photo.jpg" alt="North HQ">
</div>
<div class="rsl-gallery-overlay">
<div class="rsl-gallery-info">
<h3 class="rsl-gallery-title">North HQ</h3>
<p class="rsl-gallery-meta">Dec 1, 2025</p>
</div>
</div>
</article>
<!-- More gallery items... -->
</div>
Cards can subscribe to FilterBus for visibility filtering. Click the region buttons above to show/hide region-specific cards.
Each card has data-filter-subscribe="region" to listen to the Region filter, and data-filter-value="North" (etc.) to specify which filter value it matches. Cards without data-filter-value always show.
The container also has data-filterbus-date-field="date" to enable date range filtering. Each card has a data-date attribute with its transaction date (YYYY-MM-DD format).
Minneapolis, MN
Dec 2, 2025
$340,800
Detroit, MI
Dec 6, 2025
$125,000
Miami, FL
Dec 9, 2025
$195,600
Atlanta, GA
Dec 13, 2025
$143,500
New York, NY
Dec 16, 2025
$389,200
Boston, MA
Dec 21, 2025
$178,300
San Francisco, CA
Dec 24, 2025
$89,600
Seattle, WA
Dec 30, 2025
$67,200
<!-- Container with date filtering enabled -->
<div class="slot-layout"
data-cols-xs="1"
data-cols-md="4"
data-filterbus-date-field="date">
<!-- Card with visibility + date filtering -->
<div class="slot-item">
<div class="card"
data-filter-subscribe="region"
data-filter-value="North"
data-date="2025-12-02">
<div class="card-header">
<strong>North HQ</strong>
</div>
<div class="card-body">
<p>Minneapolis, MN</p>
<p>Dec 2, 2025</p>
<p>$340,800</p>
</div>
</div>
</div>
<!-- More cards... -->
</div>
The Star Rating Filter component creates a filter panel that integrates with FilterBus. Click a rating option to filter products by minimum star rating.
Star rating filters are commonly used in e-commerce to help users find highly-rated products. The filter publishes to FilterBus using the field parameter, and any component subscribed to that field will update.
// Create a star rating filter using JSON API V2
RSL.JSONLayout.renderLayout('#filter-container', {
version: 2,
breakpoints: { xs: 1 },
items: [{
content: {
type: "star-rating-filter",
field: "product_rating", // FilterBus field name
max: 5,
showCounts: true,
counts: { 5: 12, 4: 28, 3: 15, 2: 8, 1: 3 },
upAndAbove: true // Shows "4+ stars" format
}
}]
});
// Subscribe to filter changes
RSL.FilterBus.subscribe('my-products', function(state) {
// state.product_rating is 0 for "All", or 1-5 for star filters
const minRating = state.product_rating || 0;
const filtered = products.filter(p =>
minRating === 0 || p.rating >= minRating
);
renderProducts(filtered);
}, { keys: ['product_rating'] });
Select components can both publish selection changes AND subscribe to filter state. This makes them perfect as filter controls or for syncing with other filter components.
This Select uses data-filterbus-publish="region" to publish its selection. It also subscribes to "region" so it stays in sync when the filter buttons above change the filter state.
Use this dropdown to filter all components above (KPI Cards, Charts, Gallery, Cards):
The Select syncs with the filter buttons above. Click a button and watch the dropdown update. Or change the dropdown and watch all components filter.
<!-- Select publishes AND subscribes to FilterBus -->
<div data-rsl-select
data-rsl-select-placeholder="Select a Region..."
data-filterbus-publish="region"
data-filterbus-subscribe="region"
data-rsl-select-clearable="true">
<option value="All">All Regions</option>
<option value="North">North Region</option>
<option value="South">South Region</option>
<option value="East">East Region</option>
<option value="West">West Region</option>
</div>
<!-- Other components subscribe to "region" -->
<div data-rsl-kpi-card
data-filterbus-subscribe="region"
data-filter-value="North">
<!-- This card shows when Select is "North" or "All" -->
</div>
Tab groups can subscribe to FilterBus for visibility filtering. Click the region buttons above to show/hide region-specific tab groups. Use the date picker to filter by event date!
Each tabs-display container has data-filter-subscribe="region" to listen to the Region filter, and data-filter-value="North" (etc.) to specify which filter value it matches. Tab groups without data-filter-value always show.
Tab groups can also have data-date="2025-12-15" to filter by date range. When a date picker publishes a range, tabs outside that range are hidden.
<!-- Tab group with visibility + date filtering -->
<div class="tabs-display"
data-filter-subscribe="region"
data-filter-value="North"
data-date="2025-12-05">
<div class="slot-layout tabs" data-cols-xs="3">
<a class="slot-item tab-link" data-tab="overview">Overview</a>
<a class="slot-item tab-link" data-tab="sales">Sales</a>
<a class="slot-item tab-link" data-tab="team">Team</a>
</div>
<div class="slot-item tab-content" data-tab="overview">
<p>Overview content...</p>
</div>
<div class="slot-item tab-content" data-tab="sales">
<p>Sales content...</p>
</div>
<div class="slot-item tab-content" data-tab="team">
<p>Team content...</p>
</div>
</div>
The RSL Pagination component works with the Tables component (not Smart Table). When filters change, pagination auto-resets to page 1 to prevent users from being stranded on an invalid page.
This Pagination component is for the Tables component. The Smart Table component (shown above) has its own built-in pagination that integrates with its filtering/sorting features. Don't use both together!
The pagination subscribes to FilterBus using data-filter-subscribe="region". When the "region" filter changes, pagination receives a notification and resets to page 1. This prevents users from being on page 3 when filtered results only have 1 page of data.
| ID | Name | Region | Status | Revenue |
|---|---|---|---|---|
| 001 | Alice Johnson | North | Active | $125,000 |
| 002 | Bob Smith | South | Active | $98,500 |
| 003 | Carol White | East | Pending | $156,200 |
| 004 | David Lee | West | Active | $87,300 |
| 005 | Emma Davis | North | Active | $142,800 |
| 006 | Frank Miller | South | Inactive | $0 |
| 007 | Grace Chen | East | Active | $178,400 |
| 008 | Henry Wilson | West | Active | $92,100 |
| 009 | Ivy Taylor | North | Pending | $45,600 |
| 010 | Jack Brown | South | Active | $134,700 |
| 011 | Karen Martinez | East | Active | $167,500 |
| 012 | Leo Garcia | West | Active | $88,900 |
Click the Region filter buttons at the top of this page. When the filter changes, this pagination resets to page 1.
<!-- Regular table (NOT Smart Table) -->
<table class="rsl-table" data-paginate="5">
<thead>...</thead>
<tbody>
<tr data-region="North">...</tr>
<tr data-region="South">...</tr>
</tbody>
</table>
<!-- Pagination subscribes to "region" filter changes -->
<!-- Auto-resets to page 1 when any subscribed filter changes -->
<ul class="rsl-pagination" data-filter-subscribe="region">
<li><a href="javascript:void(0)">Previous</a></li>
<li class="active"><a href="javascript:void(0)">1</a></li>
<li><a href="javascript:void(0)">2</a></li>
<li><a href="javascript:void(0)">3</a></li>
<li><a href="javascript:void(0)">Next</a></li>
</ul>
<!-- Subscribe to multiple filters -->
<ul class="rsl-pagination" data-filter-subscribe="region,status,dateRange">
...
</ul>
Filter FAQ accordion items by category. Items matching the selected category are shown, others are hidden. Panels close automatically when hidden.
Simply include the CSS and JavaScript files in your HTML, then use the provided class names to build your layouts. RSL has zero dependencies and works with any framework or vanilla JavaScript.
Yes! RSL is built with accessibility as a core principle. All components include ARIA attributes, keyboard navigation, and screen reader support out of the box.
RSL is free and open source! There's no payment required. You can download and use it in any project, commercial or personal.
Currently RSL is entirely free. All features are included in the standard distribution with no paywalls or premium tiers.
RSL supports all modern browsers including Chrome, Firefox, Safari, Edge, and Opera. It uses CSS Grid and Flexbox which are supported in 95%+ of browsers globally.
Absolutely! RSL is framework-agnostic. You can use the CSS classes and JavaScript components directly in React, Vue, Angular, Svelte, or any other framework.
<!-- Filter control (publishes to FilterBus on change) -->
<select id="category-filter">
<option value="">All Categories</option>
<option value="general">General</option>
<option value="billing">Billing</option>
<option value="technical">Technical</option>
</select>
<script>
document.getElementById('category-filter').addEventListener('change', function() {
RSL.FilterBus.publish('category', this.value);
});
</script>
<!-- Accordion subscribes to "category" filter key -->
<div class="slot-layout classic-accordion"
data-single-open="true"
data-filterbus-key="category">
<!-- Items have data-category attributes matching the filter key -->
<div class="accordion-item" data-category="general">
<div id="faq-1" tabindex="0" class="accordion-title" role="button">
How do I get started?
</div>
<div class="accordion-content" aria-labelledby="faq-1">
<p>Content here...</p>
</div>
</div>
<div class="accordion-item" data-category="billing">
<div id="faq-2" tabindex="0" class="accordion-title" role="button">
What payment methods?
</div>
<div class="accordion-content" aria-labelledby="faq-2">
<p>Content here...</p>
</div>
</div>
</div>
The Timeline component can subscribe to FilterBus for filtering events by status, category, or type. Use the status filter below to show/hide timeline events!
Each timeline event has a data-status attribute (completed, current, upcoming) that matches the filter values. When you filter by status, only matching events are shown with smooth transitions. Also supports data-category for filtering by event category.
Initial planning, team assembly, and requirements gathering completed.
UI/UX design, wireframes, and prototypes approved by stakeholders.
Building core features, API integration, and component library.
User acceptance testing, bug fixes, and performance optimization.
Official release, marketing campaign, and customer onboarding.
<!-- Timeline subscribes to FilterBus -->
<ol data-rsl-timeline
data-timeline-variant="card"
data-filter-subscribe="timeline-status"
aria-label="Project Roadmap">
<li class="rsl-timeline-event" data-status="completed">
<div class="rsl-timeline-marker"><i class="fas fa-check"></i></div>
<div class="rsl-timeline-content">
<time class="rsl-timeline-date">Q1 2025</time>
<h3 class="rsl-timeline-title">Project Kickoff</h3>
<div class="rsl-timeline-details">
<p class="rsl-timeline-description">...</p>
</div>
</div>
</li>
<li class="rsl-timeline-event" data-status="current">
<!-- Current event -->
</li>
<li class="rsl-timeline-event" data-status="upcoming">
<!-- Upcoming event -->
</li>
</ol>
<!-- JavaScript: Publish filter on button click -->
<script>
document.querySelectorAll('.timeline-filter-btn').forEach(btn => {
btn.addEventListener('click', function() {
const status = this.dataset.status;
RSL.FilterBus.publish('timeline-status', status);
});
});
</script>
The Announcements component can subscribe to FilterBus for filtering announcements by category or priority. Try using the Region filter above - announcements are tagged with regions!
Each announcement has a data-category attribute (North, South, East, West) that matches the Region filter values. When you filter by region, only announcements from that region are shown. The component announces filter changes via screen reader live region.
Use the Region filter buttons at the top to filter these announcements:
Try clicking "North", "South", etc. in the filter above. The announcement bar will show only announcements from that region.
<!-- Announcements subscribes to FilterBus via data-filter-key -->
<div class="rsl-announcements"
data-rsl-announcements
data-auto-rotate="true"
data-interval="5000"
data-filter-key="region"
data-variant="info">
<!-- Each item has data-category matching the filter values -->
<div class="rsl-announcement-item" data-category="North" data-priority="high" data-new>
<strong>North:</strong> Q4 sales exceeded targets!
</div>
<div class="rsl-announcement-item" data-category="South" data-priority="normal">
<strong>South:</strong> Miami office renovation complete.
</div>
<div class="rsl-announcement-item" data-category="East" data-priority="high">
<strong>East:</strong> NYC shareholder meeting Dec 15th.
</div>
<div class="rsl-announcement-item" data-category="West" data-priority="normal">
<strong>West:</strong> Seattle team won innovation award!
</div>
</div>
<!-- The Region filter (already on page) publishes to FilterBus -->
<div data-rsl-filter data-filter-key="region">
<button data-filter-value="All">All</button>
<button data-filter-value="North">North</button>
<button data-filter-value="South">South</button>
<button data-filter-value="East">East</button>
<button data-filter-value="West">West</button>
</div>
Sticky Elements can publish their stuck state to FilterBus, allowing other components to react. Additionally, sticky elements can subscribe to FilterBus to show/hide based on filter state.
Publish: When a sticky element becomes stuck, it publishes true to the specified key. When unstuck, it publishes false. Other components can subscribe to react (e.g., showing a "Back to Top" button).
Subscribe: Sticky elements can filter themselves by subscribing to a FilterBus key with data-filter-subscribe and data-filter-value.
Scroll down in the box below. When the header becomes stuck, it publishes to FilterBus and the "Back to Top" button appears.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. Cras porttitor metus vel arcu sodales, ut faucibus nulla fermentum.
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora.
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum.
Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus.
Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint.
Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
The "Back to Top" button appears when the header becomes stuck and disappears when you scroll back to the top.
These sticky notification banners subscribe to the Region filter above. Try clicking "North", "South", etc. to show only notifications from that region.
Each notification has data-filter-subscribe="region" and data-filter-value matching its region.
<!-- Sticky header that publishes stuck state -->
<header data-rsl-sticky
data-sticky-position="top"
data-sticky-offset="0"
data-filterbus-publish="header-sticky-state">
Dashboard Header
</header>
<!-- Subscribe to show/hide Back to Top button -->
<script>
// When header becomes stuck, show the button
RSL.FilterBus.subscribe('back-to-top-subscriber', function(state) {
var isStuck = state['header-sticky-state'];
var button = document.getElementById('back-to-top');
button.style.display = isStuck ? 'block' : 'none';
}, { keys: ['header-sticky-state'] });
</script>
<!-- Filter controls (publisher) -->
<div data-rsl-filter data-filter-key="region">
<button data-filter-value="All">All</button>
<button data-filter-value="North">North</button>
<button data-filter-value="South">South</button>
</div>
<!-- Sticky notifications (subscribers) -->
<div data-rsl-sticky
data-filter-subscribe="region"
data-filter-value="North">
North Region: Important announcement...
</div>
<div data-rsl-sticky
data-filter-subscribe="region"
data-filter-value="South">
South Region: Regional update...
</div>
<!-- When "North" is selected, only the North notification shows -->
<!-- When "All" is selected, all notifications show -->