Webflow’s native Tabs element doesn’t support CMS content — and that makes it kind of annoying when you're trying to build fully dynamic tabs. Whether it's for FAQs, service pages, product features, or treatment categories, you quickly hit a wall if you're using Collection Lists. You can't just drop CMS content inside a native tab pane — Webflow won’t allow it.
Sure, tools like Finsweet Attributes offer a plug-and-play workaround (and we’ll mention that later too), but in this tutorial, we’ll show you how to build your own dynamic tab system using a bit of custom code and a clean Webflow setup.
Finsweet’s Attributes are great — fast to implement, reliable, and beginner-friendly. But there are a few good reasons you might want to roll your own:
This tutorial walks you through everything — from CMS setup to layout to JavaScript — so by the end, you’ll have a fully functional, scalable tab component that works dynamically with your CMS content.
No plugins. Just Webflow + a sprinkle of JavaScript magic.
Live demo:
👉 https://cms-tabs-without-finsweet.webflow.io/
Clone it:
👉 Cloneable Webflow Project
We’ll build a Product Features Tabs Section powered by a CMS collection. Each tab will represent a product feature (like "Speed", "Security", "Integrations", etc.), and when clicked, it’ll show the full description from the CMS.
Product Features
We won’t use Webflow’s native Tabs component at all. Instead, we’ll use Collection Lists to generate both the tab buttons and tab content — linked together using the CMS Slug field and a couple of custom attributes.
data-tab
and data-tab-content
attributesTabs Section
Tabs Wrapper
Tab Buttons Wrapper
Tab Content Wrapper
Tab Buttons Wrapper
, add a Collection Listtabs-nav
data-tab
tabs-nav is-active
Tab Content Wrapper
, add another Collection ListProduct Features
collectiontab-content-block
data-tab-content
display: none
by default.is-active
for the visible block (display: block
or use Webflow interactions)Now it’s time to make it all work.
This JavaScript connects the tab buttons to the matching content blocks. It also shows the first tab by default and supports deep linking via URL hash.
.tabs-nav
.is-active
to the correct tab and content#slug
in the URLdata-tab="{{ Slug }}"
data-tab-content="{{ Slug }}"
.is-active
for both tabs and contentI’m using the Slug field for both data-tab
and data-tab-content
. It just works better — cleaner URLs like #speed-optimization
, no weird characters, and it's perfect for linking tabs.
I also added two optional extras to the script:
#slug
in the URLThey’re both clearly marked in the code, so if you don’t need them, just delete or comment those parts out. The main tabs still work great without them.
Live demo:
👉 https://cms-tabs-without-finsweet.webflow.io/
Clone it:
👉 Cloneable Webflow Project