How to Add a Responsive Post Slider with Category Filters in Blogger
If you want to make your Blogger website look more professional and modern, adding a responsive post slider is one of the best ways to do it. A post slider automatically fetches your latest published posts from a specific label (category) and displays them in an attractive, scrollable card carousel — complete with thumbnails, titles, publish dates, and estimated reading time.
In this guide, you’ll learn exactly how to add a fully functional, mobile-friendly post slider to your Blogger website using a single block of HTML/CSS/JavaScript code.
Here’s what the slider looks like when it’s working:
- 3 cards per row on desktop, 2 on tablet, 1 on mobile
- Auto-slides every 4 seconds
- Touch/swipe support for mobile users
- Navigation arrows and dot indicators
- Hover effects with smooth image zoom
- Reads posts dynamically from your Blogger feed by label

How to add Post slider to Blogger website.
For this, Login to Blogger dashboard and go to layout section.
Identiy the section where you want to add this slider and you need to add a custom HTML widget.
If there is no option to add more widgets then first create a section with minimum 2-3 widgets option. Follow the video to learn more about How to create New widget section in Blogger.
Once you Create the widgets section, Add a HTML / Javascript widgets and use the below code.
<style>
.kb-slider-container {
--kb-gap: 20px;
--kb-radius: 12px;
--kb-primary: #2563eb;
--kb-text-main: #1f2937;
--kb-text-muted: #6b7280;
--kb-bg: #ffffff;
--kb-card-bg: #f6f8fa;
--kb-arrow-bg: rgba(255, 255, 255, 0.9);
--kb-arrow-color: #374151;
--kb-dot-color: #d1d5db;
--kb-dot-active: #2563eb;
position: relative;
width: 100%;
max-width: 1200px;
margin: 0 auto;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
box-sizing: border-box;
}
.kb-slider-container *,
.kb-slider-container *::before,
.kb-slider-container *::after {
box-sizing: inherit;
}
.kb-wrapper {
overflow: hidden;
width: 100%;
touch-action: pan-y;
}
.kb-track {
display: flex;
will-change: transform;
transform: translateZ(0);
backface-visibility: hidden;
}
.kb-track.kb-animating {
transition: transform 500ms cubic-bezier(0.4, 0.0, 0.2, 1);
}
.kb-slide {
flex-shrink: 0;
padding: 0 calc(var(--kb-gap) / 2);
}
.kb-card {
position: relative;
display: flex;
flex-direction: column;
height: 100%;
background: var(--kb-card-bg);
border-radius: var(--kb-radius);
overflow: hidden;
box-shadow: var(--kb-shadow);
text-decoration: none;
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
user-select: none;
}
.kb-card:hover,
.kb-card:focus-within {
transform: translateY(-5px);
box-shadow: var(--kb-shadow-hover);
}
.kb-image-container {
position: relative;
width: 100%;
padding-top: 56.25%; /* 16:9 Aspect Ratio */
overflow: hidden;
background-color: #f3f4f6;
}
.kb-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.kb-card:hover .kb-image {
transform: scale(1.05);
}
.kb-category {
position: absolute;
top: 15px;
left: 15px;
background-color: var(--kb-primary);
color: #ffffff;
font-size: 0.75rem;
font-weight: 600;
padding: 4px 10px;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.05em;
z-index: 2;
}
.kb-content {
padding: 20px;
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: space-between;
}
.kb-title {
font-size: 1.25rem;
font-weight: 700;
color: var(--kb-text-main);
line-height: 1.4;
margin: 0 0 15px 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.kb-meta {
display: flex;
align-items: center;
font-size: 0.85rem;
color: var(--kb-text-muted);
font-weight: 500;
}
.kb-date, .kb-readtime {
display: flex;
align-items: center;
}
.kb-meta-separator {
margin: 0 8px;
width: 4px;
height: 4px;
background-color: var(--kb-text-muted);
border-radius: 50%;
}
.kb-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
background: var(--kb-arrow-bg);
border: none;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
color: var(--kb-arrow-color);
transition: background 0.2s, color 0.2s, transform 0.2s;
}
.kb-arrow:hover {
background: var(--kb-primary);
color: #ffffff;
}
.kb-prev { left: 10px; }
.kb-next { right: 10px; }
.kb-arrow svg {
width: 20px;
height: 20px;
fill: currentColor;
}
.kb-pagination {
display: flex;
justify-content: center;
align-items: center;
padding-top: 20px;
padding-bottom: 20px;
gap: 8px;
}
.kb-dot {
width: 10px;
height: 10px;
background-color: var(--kb-dot-color);
border-radius: 50%;
border: none;
cursor: pointer;
padding: 0;
transition: background-color 0.3s, transform 0.3s;
}
.kb-dot.kb-active {
background-color: var(--kb-dot-active);
transform: scale(1.2);
}
.kb-dot:focus-visible,
.kb-arrow:focus-visible,
.kb-card:focus-visible {
outline: 2px solid var(--kb-primary);
outline-offset: 2px;
}
.kb-loader {
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
font-size: 1rem;
color: var(--kb-text-muted);
}
</style>
<div id="kb-slider-instance" class="kb-slider-container"></div>
<script>
(function() {
'use strict';
const CONFIG = {
blog: "https://backlinkidea.blogspot.com",
label: "Marketing",
posts: 6,
desktop: 3,
tablet: 2,
mobile: 1,
autoplay: true,
autoplaySpeed: 4000,
animationSpeed: 500,
showDate: true,
showCategory: true,
showDots: true,
showArrows: true,
showReadTime: true,
pauseOnHover: true,
loop: true,
touchSwipe: true,
mouseDrag: true,
imageRatio: "16:9",
targetImageSize: "s1600"
};
const Utils = {
debounce: (func, wait) => {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
},
decodeHTML: (html) => {
const txt = document.createElement("textarea");
txt.innerHTML = html;
return txt.value;
},
getPlaceholderSVG: () => {
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 9"><rect width="16" height="9" fill="#e5e7eb"/><path d="M5.5 4.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm6.5 3l-3-4-2.5 3-1.5-2-3 4h10z" fill="#9ca3af"/></svg>`;
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
}
};
class ImageParser {
static getHighResUrl(url, targetSize = 's1600') {
if (!url) return '';
return url
.replace(/\/w\d+-h\d+(-[a-zA-Z0-9-]+)?\//i, `/${targetSize}/`)
.replace(/\/s\d+(-[a-zA-Z0-9-]+)?\//i, `/${targetSize}/`)
.replace(/=(s|w|h)\d+[^&]*/i, `=${targetSize}`);
}
static extract(post, config) {
let imageUrl = '';
if (post.media$thumbnail && post.media$thumbnail.url) {
imageUrl = post.media$thumbnail.url;
} else if (post.content && post.content.$t) {
const match = post.content.$t.match(/<img.+?src="([^"]+)"/i);
if (match && match[1]) imageUrl = match[1];
} else if (post.summary && post.summary.$t) {
const match = post.summary.$t.match(/<img.+?src="([^"]+)"/i);
if (match && match[1]) imageUrl = match[1];
}
if (!imageUrl) {
return Utils.getPlaceholderSVG();
}
return ImageParser.getHighResUrl(imageUrl, config.targetImageSize || 's1600');
}
}
class DateFormatter {
static format(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
}
}
class ReadingTime {
static calculate(htmlContent) {
if (!htmlContent) return '1 min read';
const text = htmlContent.replace(/<[^>]+>/g, '');
const wordCount = text.split(/\s+/).filter(w => w.length > 0).length;
return `${Math.ceil(wordCount / 200)} min read`;
}
}
class KBSlider {
constructor(elementId, userConfig = {}) {
this.container = document.getElementById(elementId);
if (!this.container) return;
this.config = { ...CONFIG, ...userConfig };
this.instanceId = Math.random().toString(36).substring(2, 9);
this.posts = [];
this.cardsPerView = 3;
this.currentIndex = 0;
this.isDragging = false;
this.isAnimating = false;
this.startPos = 0;
this.currentTranslate = 0;
this.prevTranslate = 0;
this.animationID = null;
this.slideWidth = 0;
this.autoplayTimer = null;
this.totalActualSlides = 0;
this.init();
}
init() {
this.showLoader();
this.fetchBloggerData();
}
showLoader() {
this.container.innerHTML = `<div class="kb-loader">Loading posts...</div>`;
}
fetchBloggerData() {
const callbackName = `kb_callback_${this.instanceId}`;
window[callbackName] = (data) => {
this.processData(data);
delete window[callbackName];
const scriptTag = document.getElementById(`kb-script-${this.instanceId}`);
if (scriptTag) scriptTag.remove();
};
const baseUrl = this.config.blog.replace(/\/$/, '');
const feedUrl = `${baseUrl}/feeds/posts/default/-/${encodeURIComponent(this.config.label)}?alt=json-in-script&max-results=${this.config.posts}&callback=${callbackName}`;
const script = document.createElement('script');
script.src = feedUrl;
script.id = `kb-script-${this.instanceId}`;
script.onerror = () => {
this.container.innerHTML = `<div class="kb-loader">Unable to load feed from ${this.config.blog} for label "${this.config.label}".</div>`;
};
document.body.appendChild(script);
}
processData(data) {
if (!data || !data.feed || !data.feed.entry) {
this.container.innerHTML = `<div class="kb-loader">No posts found for "${this.config.label}".</div>`;
return;
}
this.posts = data.feed.entry.map(entry => {
const link = entry.link.find(l => l.rel === 'alternate');
return {
title: Utils.decodeHTML(entry.title.$t),
url: link ? link.href : '#',
image: ImageParser.extract(entry, this.config),
category: (entry.category && entry.category[0]) ? entry.category[0].term : this.config.label,
date: DateFormatter.format(entry.published.$t),
readTime: ReadingTime.calculate(entry.content ? entry.content.$t : (entry.summary ? entry.summary.$t : ''))
};
});
this.totalActualSlides = this.posts.length;
this.buildDOM();
this.setupSlider();
}
buildDOM() {
this.container.innerHTML = '';
this.wrapper = document.createElement('div');
this.wrapper.className = 'kb-wrapper';
this.track = document.createElement('div');
this.track.className = 'kb-track';
this.generateSlides();
this.wrapper.appendChild(this.track);
this.container.appendChild(this.wrapper);
if (this.config.showArrows && this.totalActualSlides > 1) this.buildArrows();
if (this.config.showDots && this.totalActualSlides > 1) this.buildDots();
}
generateSlides() {
this.track.innerHTML = '';
this.updateCardsPerView();
const createCardHTML = (post, isClone = false) => {
const ariaHidden = isClone ? 'aria-hidden="true" tabindex="-1"' : '';
const metaHTML = [];
if (this.config.showDate) metaHTML.push(`<span class="kb-date">${post.date}</span>`);
if (this.config.showDate && this.config.showReadTime) metaHTML.push(`<span class="kb-meta-separator"></span>`);
if (this.config.showReadTime) metaHTML.push(`<span class="kb-readtime">${post.readTime}</span>`);
return `
<div class="kb-slide">
<a href="${post.url}" class="kb-card" ${ariaHidden} aria-label="Read: ${post.title}">
<div class="kb-image-container">
<img class="kb-image" src="${post.image}" alt="${post.title}" loading="lazy">
${this.config.showCategory ? `<span class="kb-category">${post.category}</span>` : ''}
</div>
<div class="kb-content">
<h3 class="kb-title">${post.title}</h3>
<div class="kb-meta">${metaHTML.join('')}</div>
</div>
</a>
</div>
`;
};
let slidesHTML = [];
if (this.config.loop && this.totalActualSlides > this.cardsPerView) {
this.posts.slice(-this.cardsPerView).forEach(p => slidesHTML.push(createCardHTML(p, true)));
}
this.posts.forEach(p => slidesHTML.push(createCardHTML(p, false)));
if (this.config.loop && this.totalActualSlides > this.cardsPerView) {
this.posts.slice(0, this.cardsPerView).forEach(p => slidesHTML.push(createCardHTML(p, true)));
}
this.track.innerHTML = slidesHTML.join('');
this.slides = Array.from(this.track.querySelectorAll('.kb-slide'));
}
buildArrows() {
const prevBtn = document.createElement('button');
prevBtn.className = 'kb-arrow kb-prev';
prevBtn.setAttribute('aria-label', 'Previous Slide');
prevBtn.innerHTML = `<svg viewBox="0 0 24 24"><path d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6 6 6 1.41-1.41z"/></svg>`;
const nextBtn = document.createElement('button');
nextBtn.className = 'kb-arrow kb-next';
nextBtn.setAttribute('aria-label', 'Next Slide');
nextBtn.innerHTML = `<svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg>`;
this.container.appendChild(prevBtn);
this.container.appendChild(nextBtn);
prevBtn.addEventListener('click', () => this.slidePrev());
nextBtn.addEventListener('click', () => this.slideNext());
}
buildDots() {
this.pagination = document.createElement('div');
this.pagination.className = 'kb-pagination';
for (let i = 0; i < this.totalActualSlides; i++) {
const dot = document.createElement('button');
dot.className = 'kb-dot';
dot.setAttribute('aria-label', `Go to slide ${i + 1}`);
dot.addEventListener('click', () => this.goToSlide(i));
this.pagination.appendChild(dot);
}
this.container.appendChild(this.pagination);
this.updateDots();
}
setupSlider() {
if (this.totalActualSlides === 0) return;
this.updateDimensions();
this.currentIndex = 0;
this.updatePosition(false);
this.attachEvents();
this.startAutoplay();
}
updateCardsPerView() {
const w = window.innerWidth;
this.cardsPerView = w >= 1024 ? this.config.desktop : (w >= 768 ? this.config.tablet : this.config.mobile);
if (this.totalActualSlides < this.cardsPerView) this.cardsPerView = this.totalActualSlides;
}
updateDimensions() {
this.updateCardsPerView();
this.slideWidth = this.wrapper.clientWidth / this.cardsPerView;
this.slides.forEach(s => s.style.width = `${this.slideWidth}px`);
}
getOffset() {
return (this.config.loop && this.totalActualSlides > this.cardsPerView) ? this.cardsPerView : 0;
}
updatePosition(animate = true) {
const offsetIndex = this.currentIndex + this.getOffset();
this.currentTranslate = -(offsetIndex * this.slideWidth);
this.prevTranslate = this.currentTranslate;
if (animate) {
this.isAnimating = true;
this.track.classList.add('kb-animating');
this.track.style.transitionDuration = `${this.config.animationSpeed}ms`;
} else {
this.isAnimating = false;
this.track.classList.remove('kb-animating');
this.track.style.transitionDuration = '0ms';
}
this.setTransform(this.currentTranslate);
this.updateDots();
}
setTransform(val) {
this.track.style.transform = `translate3d(${val}px, 0, 0)`;
}
slideNext() {
if (this.isAnimating) return;
if (!this.config.loop && this.currentIndex >= this.totalActualSlides - this.cardsPerView) return;
this.currentIndex++;
this.updatePosition(true);
this.resetAutoplay();
}
slidePrev() {
if (this.isAnimating) return;
if (!this.config.loop && this.currentIndex <= 0) return;
this.currentIndex--;
this.updatePosition(true);
this.resetAutoplay();
}
goToSlide(index) {
if (this.isAnimating) return;
this.currentIndex = index;
this.updatePosition(true);
this.resetAutoplay();
}
updateDots() {
if (!this.config.showDots || !this.pagination) return;
const dots = Array.from(this.pagination.querySelectorAll('.kb-dot'));
dots.forEach(d => d.classList.remove('kb-active'));
let idx = (this.currentIndex % this.totalActualSlides + this.totalActualSlides) % this.totalActualSlides;
if (dots[idx]) dots[idx].classList.add('kb-active');
}
startAutoplay() {
if (!this.config.autoplay) return;
this.stopAutoplay();
this.autoplayTimer = setInterval(() => this.slideNext(), this.config.autoplaySpeed);
}
stopAutoplay() {
if (this.autoplayTimer) clearInterval(this.autoplayTimer);
}
resetAutoplay() {
this.startAutoplay();
}
attachEvents() {
window.addEventListener('resize', Utils.debounce(() => {
this.updateDimensions();
this.updatePosition(false);
}, 250));
if (this.config.touchSwipe) {
this.wrapper.addEventListener('touchstart', (e) => this.dragStart(e), { passive: true });
this.wrapper.addEventListener('touchmove', (e) => this.dragAction(e), { passive: false });
this.wrapper.addEventListener('touchend', () => this.dragEnd());
}
if (this.config.mouseDrag) {
this.wrapper.addEventListener('mousedown', (e) => this.dragStart(e));
this.wrapper.addEventListener('mousemove', (e) => this.dragAction(e));
this.wrapper.addEventListener('mouseup', () => this.dragEnd());
this.wrapper.addEventListener('mouseleave', () => { if (this.isDragging) this.dragEnd(); });
this.wrapper.addEventListener('dragstart', (e) => e.preventDefault());
}
if (this.config.pauseOnHover && this.config.autoplay) {
this.container.addEventListener('mouseenter', () => this.stopAutoplay());
this.container.addEventListener('mouseleave', () => this.startAutoplay());
}
this.container.setAttribute('tabindex', '0');
this.container.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') this.slidePrev();
if (e.key === 'ArrowRight') this.slideNext();
});
this.track.addEventListener('transitionend', (e) => {
if (e.target !== this.track) return;
this.track.classList.remove('kb-animating');
this.isAnimating = false;
if (this.config.loop && this.totalActualSlides > this.cardsPerView) {
if (this.currentIndex >= this.totalActualSlides) {
this.currentIndex = 0;
this.updatePosition(false);
} else if (this.currentIndex < 0) {
this.currentIndex = this.totalActualSlides - 1;
this.updatePosition(false);
}
}
});
}
dragStart(e) {
if (this.isAnimating) return;
this.isDragging = true;
this.startPos = e.type.includes('mouse') ? e.pageX : e.touches[0].clientX;
this.track.classList.remove('kb-animating');
this.stopAutoplay();
}
dragAction(e) {
if (!this.isDragging) return;
const currentPos = e.type.includes('mouse') ? e.pageX : e.touches[0].clientX;
const diff = currentPos - this.startPos;
if (e.type.includes('touch') && Math.abs(diff) < 10) return;
if (e.cancelable) e.preventDefault();
const move = this.prevTranslate + diff;
if (this.animationID) cancelAnimationFrame(this.animationID);
this.animationID = requestAnimationFrame(() => this.setTransform(move));
}
dragEnd() {
if (!this.isDragging) return;
this.isDragging = false;
cancelAnimationFrame(this.animationID);
const transformMatrix = window.getComputedStyle(this.track).transform;
if (transformMatrix !== 'none') {
const currentTranslateX = parseFloat(transformMatrix.split(',')[4]);
const movedBy = currentTranslateX - this.prevTranslate;
const threshold = this.slideWidth * 0.2;
if (movedBy < -threshold) this.slideNext();
else if (movedBy > threshold) this.slidePrev();
else this.updatePosition(true);
}
this.resetAutoplay();
}
}
document.addEventListener('DOMContentLoaded', () => {
new KBSlider('kb-slider-instance');
});
})();
</script>
Now replace the Blog URL, category name and customize the Card background, button color and show or hide different section like dots, arrow, auto scroll speed and more.
Configure the Slider for Your Blog
Inside the JavaScript <script> block, find the CONFIG: object near the top. It looks like this:
const CONFIG = {
blog: "https://backlinkidea.blogspot.com",
label: "Marketing",
posts: 6,
desktop: 3,
tablet: 2,
mobile: 1,
autoplay: true,
autoplaySpeed: 4000,
...
};
Change these settings to match your blog:
| Setting | What It Does | Example Value |
|---|---|---|
| blog | Your blog’s full URL | “https://yourblog.blogspot.com” |
| label | The post label/category to display | “Marketing” |
| posts | Total number of posts to load | 6 |
| desktop | Cards visible on desktop (≥1024px) | 3 |
| tablet | Cards visible on tablet (≥768px) | 2 |
| mobile | Cards visible on mobile (<768px) | 1 |
| autoplay | Enable auto-sliding | true or false |
| autoplaySpeed | Milliseconds between slides | 4000 (= 4 seconds) |
| loop | Loop back to beginning | true or false |
| showDate | Show publish date on cards | true or false |
| showReadTime | Show estimated reading time | true or false |
| showArrows | Show previous/next arrow buttons | true or false |
| showDots | Show dot navigation indicators | true or false |
Follow this video to learn the complete step by step instructions.
Touch and Keyboard Navigation
The slider supports multiple interaction methods out of the box:
- Mouse drag — click and drag left/right to slide
- Touch swipe — swipe left/right on mobile devices
- Arrow buttons — click the
‹and›buttons - Dot indicators — click any dot to jump to that slide
- Keyboard — focus the slider and press
←/→arrow keys - Autoplay pause — hovering your mouse over the slider pauses autoplay
Troubleshooting Common Issues
Slider shows “No posts found for [label]”
- Make sure the label name in CONFIG.label exactly matches the label in your Blogger posts (including capitalization)
- Verify that posts with that label are published, not drafts
Images are not showing / showing a grey placeholder
- Some posts may not have a featured image. The slider falls back to a grey placeholder SVG automatically
- Add a featured image to your posts for best results
Limit posts to a reasonable number. Fetching 6–9 posts is ideal. Loading too many (e.g., 20+) can slow down the initial page load.
Conclusion
Adding a responsive post slider to your Blogger website is a powerful way to increase engagement, showcase your best content, and give your blog a more polished, magazine-style look. The code covered in this guide is self-contained, lightweight, and works entirely within Blogger’s existing infrastructure.
Once installed, just keep publishing posts under your chosen label and the slider will automatically include your latest content every time the page loads.
Happy blogging! 🚀



