To build a custom dropdown in Webflow without the Navbar component, create a trigger Div Block and a hidden list Div Block as siblings. Set the list to Display: None initially. Use the Interactions panel (H) → Element trigger → Mouse Click (Tap) → 1st click: Show list (Display Block, Opacity 0→1) → 2nd click: Hide list. This pattern works for filter menus, sort selectors, language switchers, and any custom UI control that needs a click-triggered floating panel.
Webflow Custom Dropdowns: Click-Triggered Menus Without the Navbar Component
Webflow's built-in Dropdown element (used inside navbars) is great for navigation menus, but many UI patterns need dropdowns outside of a navbar: filter menus, sort-by selectors, language/currency switchers, custom select inputs, and floating option panels. For these use cases, you build a dropdown from scratch using Div Blocks and Webflow's Interactions system. This tutorial covers the complete pattern — building the trigger and list structure, using the Mouse Click (Tap) 1st/2nd click toggle, animating the open/close with smooth transitions, positioning the dropdown panel with Absolute positioning, and handling click-outside-to-close behavior with custom code. This is fundamentally different from the Navbar Dropdown tutorial, which covers the purpose-built Navbar component.
Prerequisites
- A Webflow project open in the Designer
- Basic familiarity with the Interactions panel (H) and how to set Element triggers
- The technique is plan-agnostic for visual-only dropdowns; click-outside-to-close requires Basic plan or above for custom code
Step-by-step guide
Build the dropdown container structure
Build the dropdown container structure
Open the Add Elements panel (A) → Structure → Div Block and add it to the canvas. Give it a class name like 'dropdown-wrapper'. This wrapper is the positioning anchor — it will be the parent for both the trigger and the floating list. Inside 'dropdown-wrapper', add two child Div Blocks: one with class 'dropdown-trigger' (the visible button the user clicks) and one with class 'dropdown-list' (the panel that appears on click). In the Navigator (Z), confirm the hierarchy is: dropdown-wrapper → dropdown-trigger + dropdown-list. This parent-child relationship is essential for absolute positioning to work correctly.
Expected result: Navigator shows dropdown-wrapper containing dropdown-trigger and dropdown-list as siblings at the same level.
Style the trigger element
Style the trigger element
Select 'dropdown-trigger' → Style Panel (S) → Layout → Display: Flex → Justify: Space Between → Align: Center. Set a fixed Width and Height (e.g., 200px × 44px) or let it size to content with padding. Add Backgrounds → solid color. Add Borders → 1px solid border. Add Border Radius (4-8px for a slight curve). Inside 'dropdown-trigger', add a Text element (Add Elements A → Basic → Text) for the label (e.g., 'Select option') and an SVG or Unicode chevron character for the arrow indicator. Style Panel → Effects → Transitions → '+' → Transform → 300ms → Ease — this will animate the chevron rotation when the dropdown opens.
Expected result: A styled, button-like trigger element with a label and chevron arrow is visible on the canvas.
Position the dropdown list panel with Absolute positioning
Position the dropdown list panel with Absolute positioning
Select the 'dropdown-wrapper' container → Style Panel (S) → Position → Relative. This creates the positioning context for the absolute child. Now select 'dropdown-list' → Style Panel → Position → Absolute. Set Top: 100% (places the list directly below the trigger), Left: 0, Width: 100% (matches the trigger width) or a custom pixel width (e.g., 240px). Set Z-index: 100 to ensure the dropdown appears above other page content. Add a Background color, Border, Border Radius, and Box Shadow (Effects → Box Shadows: X:0, Y:4px, Blur:16px, Color: rgba(0,0,0,0.12)) to give the panel a floating card appearance.
Expected result: The dropdown list panel floats directly below the trigger with a card shadow. It is overlapping other content because it is absolutely positioned.
Populate the dropdown list with options
Populate the dropdown list with options
Inside 'dropdown-list', add individual option items. Each option should be a Div Block with class 'dropdown-item'. Inside each 'dropdown-item', add a Text element with the option label. Style each 'dropdown-item': Style Panel → Layout → Display: Flex → Align: Center. Set Padding: 10px 16px. Add a Hover state: Selector States → Hover → Backgrounds → lighter brand color. For a link-type dropdown (like a navigation-style menu), use Link Block (Add Elements A → Basic → Link) instead of Div Block for each option and assign destinations in Element Settings (D).
Expected result: The dropdown list panel contains multiple styled options with hover states. The list looks like a complete floating select menu.
Set the initial hidden state
Set the initial hidden state
The dropdown list must be hidden by default. Select 'dropdown-list' → Style Panel (S) → Effects → Opacity: 0 (this will be animated during open). Also go to Style Panel → Layout → Display → None — this removes it from layout flow completely when hidden. IMPORTANT: Do not only set Display: None — Webflow's Interactions cannot transition from Display: None to visible with a smooth animation. Instead, set Display: None as a starting state in the Interaction (not the Style Panel). For the Style Panel, keep Display: Block but Opacity: 0 and Style Panel → Position → set Visibility to 'hidden' via Custom Properties if needed, OR handle the initial state purely through the Interaction's 'Set Initial Appearance' step.
Expected result: The dropdown list is invisible (opacity 0, slightly offset upward) but still in the layout. It is ready to be animated open by the Interaction.
Build the open/close Interaction
Build the open/close Interaction
Select the 'dropdown-trigger' element. Open the Interactions panel (H) → Element triggers → '+' → Mouse Click (Tap). Webflow creates two animation slots: 1st Click and 2nd Click — this is the built-in toggle mechanism. For 1st Click (open): click '+' → Start Animation → name it 'Dropdown Open'. Add 'dropdown-list' as the target (use 'Affect Different Element' → select class 'dropdown-list'). Set: Opacity from 0 to 1 (0.25s, Ease Out) + Transform Move Y from -8px to 0px (0.25s, Ease Out). For 2nd Click (close): click '+' → Start Animation → name it 'Dropdown Close'. Target 'dropdown-list' again. Set: Opacity from 1 to 0 (0.2s, Ease In) + Move Y from 0 to -8px. Also rotate the chevron icon in each animation: 1st click rotates 180°, 2nd click rotates back to 0°.
Expected result: Clicking the trigger smoothly opens the dropdown list with a fade and slide-down animation. Clicking again closes it with a fade and slide-up. The chevron rotates to indicate open/closed state.
Add click-outside-to-close with custom JavaScript
Add click-outside-to-close with custom JavaScript
Webflow's Interactions cannot detect clicks outside an element natively. For production-quality dropdowns, add a small JavaScript snippet to close the dropdown when the user clicks anywhere else on the page. Add an HTML Embed element to the page (Add Elements A → Basic → HTML Embed) or add the script to Page Settings → Before </body> Code. The script listens for document clicks and closes the dropdown if the click target is outside the dropdown wrapper. Requires a paid plan (Basic or above) for custom code to work on the published site.
1<!-- Add to Page Settings → Before </body> Code -->2<script>3// Close custom dropdown when clicking outside it4document.addEventListener('click', function(event) {5 // Select all dropdown wrappers on the page6 var dropdowns = document.querySelectorAll('[data-dropdown]');78 dropdowns.forEach(function(dropdown) {9 var list = dropdown.querySelector('.dropdown-list');10 var trigger = dropdown.querySelector('.dropdown-trigger');1112 if (!dropdown.contains(event.target)) {13 // Click was outside — close the dropdown14 if (list) {15 list.style.opacity = '0';16 list.style.pointerEvents = 'none';17 list.style.transform = 'translateY(-8px)';18 }19 // Reset the open state in Webflow's interaction system20 // This ensures the 1st/2nd click counter resets21 if (trigger && trigger.getAttribute('data-open') === 'true') {22 trigger.click(); // Trigger a simulated 2nd click to reset23 trigger.setAttribute('data-open', 'false');24 }25 } else if (trigger && trigger.contains(event.target)) {26 var isOpen = trigger.getAttribute('data-open') === 'true';27 trigger.setAttribute('data-open', !isOpen ? 'true' : 'false');28 }29 });30});31</script>Expected result: Clicking anywhere outside the dropdown wrapper closes the dropdown. The open/close animation still plays correctly when triggered by the button.
Complete working example
1// WEBFLOW CUSTOM DROPDOWN — Click Outside to Close2// Add this to: Page Settings → Before </body> Code3// OR: Project Settings → Footer Code (for site-wide use)4//5// Requirements:6// - Dropdown wrapper div has custom attribute: data-dropdown7// - Trigger div has class: dropdown-trigger8// - List div has class: dropdown-list9// - Initial state: dropdown-list has opacity:0 and pointer-events:none set10// via Custom Properties in Style Panel1112(function() {13 'use strict';1415 var openDropdown = null;1617 // Listen for all document clicks18 document.addEventListener('click', function(e) {19 var clickedWrapper = e.target.closest('[data-dropdown]');2021 // Close previously open dropdown if different one was clicked22 if (openDropdown && openDropdown !== clickedWrapper) {23 closeDropdown(openDropdown);24 openDropdown = null;25 }2627 // If clicked inside a dropdown wrapper, let Webflow interactions handle it28 // Track which dropdown is now open29 if (clickedWrapper) {30 var trigger = clickedWrapper.querySelector('.dropdown-trigger');31 if (trigger && e.target.closest('.dropdown-trigger')) {32 openDropdown = clickedWrapper;33 }34 }35 });3637 function closeDropdown(wrapper) {38 var list = wrapper.querySelector('.dropdown-list');39 if (!list) return;40 // Apply closing styles — mirrors the 2nd-click Webflow animation41 list.style.transition = 'opacity 0.2s ease-in, transform 0.2s ease-in';42 list.style.opacity = '0';43 list.style.transform = 'translateY(-8px)';44 list.style.pointerEvents = 'none';45 }4647})();Common mistakes
Why it's a problem: Dropdown list appears in the wrong position or behind other elements
How to avoid: Verify the parent 'dropdown-wrapper' has Position: Relative (Style Panel → Position → Relative). Without this, the dropdown-list uses the next positioned ancestor as its reference, which may be the body or another container. Also check Z-index: Style Panel → Position → Z-index: 100 on the dropdown-list.
Why it's a problem: The dropdown opens on first click but does not close on second click
How to avoid: Webflow's 1st/2nd click counter can get desynchronized. This happens if you use programmatic clicks (from JavaScript) that do not cleanly reset the counter. If using the click-outside script above, check that simulated trigger.click() calls are not firing an extra click count. The safest fix is to use a custom data attribute to track open state independently of Webflow's counter.
Why it's a problem: The dropdown list is visible in the Designer but should start hidden
How to avoid: Set the initial state in the Interaction, not just in the Style Panel. In the Interactions panel (H) → select the Mouse Click Tap interaction → click '...' → 'Set Initial Appearance'. Set Opacity: 0 and Move Y: -8px here. This makes the Interaction system responsible for the initial state, preventing the element from being visible before the first user interaction.
Why it's a problem: Custom dropdown options are not clickable after the dropdown opens
How to avoid: If you set pointer-events:none on the dropdown-list as an initial state, you must also set pointer-events:auto in the 'Dropdown Open' interaction animation. Add it as a Style Property in the animation: Property → pointer-events → Value: auto. Without this, clicks on the dropdown items pass through to elements behind the list.
Best practices
- Always set Position: Relative on the 'dropdown-wrapper' parent before setting Position: Absolute on the 'dropdown-list' — without a Relative parent, Absolute positioning references the nearest positioned ancestor, which is often the body.
- Use Opacity + Move Y Transform animations for dropdown open/close instead of Display None toggling — CSS transitions cannot animate between display:none and display:block, but Opacity + Transform create smooth animations.
- Set pointer-events: none on the closed dropdown via Custom Properties to prevent users from accidentally clicking invisible dropdown items before the animation completes.
- Add a chevron icon rotation to the trigger (Rotate 0° → 180°) as part of the open/close interaction — this visual indicator communicates the dropdown's state to users.
- For CMS-connected dropdown options (e.g., a filter dropdown with categories from a Collection), use Webflow's built-in Select element for form contexts, and a custom Div Block dropdown with CMS binding for display-only filter UIs.
- Test dropdown positioning at all breakpoints — absolute-positioned dropdowns can overflow the viewport edges on Mobile. Add responsive width adjustments at the Mobile breakpoint.
- Always add a click-outside-to-close handler for production sites — without it, the only way to close the dropdown is clicking the trigger again, which is non-intuitive UX.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a custom dropdown filter menu in Webflow (not inside a Navbar). I have a trigger button and a hidden list of options below it. I want the dropdown to open when the trigger is clicked, close when clicked again, and close when the user clicks anywhere else on the page. Explain the Webflow Interactions setup (Mouse Click 1st/2nd click) and the JavaScript I need for click-outside detection. Reference Webflow panel names.
In my Webflow project I have a product filter section with a custom dropdown built from Div Blocks. The dropdown opens correctly using a Click interaction, but when I click an option inside the dropdown, the option is not responding to clicks — it seems like pointer events are being blocked. How do I fix this in Webflow's Style Panel and Interactions panel?
Frequently asked questions
What is the difference between Webflow's built-in Dropdown element and a custom dropdown?
Webflow's built-in Dropdown element (Add Elements → Advanced → Dropdown) is part of the Navbar component and generates semantic navigation HTML with built-in click/hover toggling. A custom dropdown built from Div Blocks and Interactions has no semantic meaning by default but is fully customizable in appearance and behavior. Use the built-in Dropdown for navigation menus; use custom Div Block dropdowns for filter panels, sort controls, and non-navigation UI.
Can Webflow custom dropdowns be CMS-connected?
Yes, but with limitations. Inside a Collection List, the dropdown-item Div Blocks can be bound to CMS fields — their text, color, and link destinations can come from CMS data. However, using a custom dropdown as a filter that dynamically filters the visible collection items requires a third-party tool like Finsweet CMS Filter, since Webflow does not have native CMS filtering tied to click events.
Does the click-outside-to-close JavaScript work in Webflow's preview mode?
No. JavaScript added via Page Settings code or HTML Embed elements does not execute in the Webflow Designer preview. You must publish your site (or use a staging subdomain) to test JavaScript behavior. Use the Preview button (eye icon, top right) as a test for visual layout only, and test JavaScript on the published URL.
How do I make multiple custom dropdowns on the same page without each one requiring a separate interaction?
Set the Interaction trigger scope to 'Class' in the Mouse Click (Tap) settings. With class-scoped interactions, all elements sharing the 'dropdown-trigger' class use the same interaction definition. However, each instance opens/closes independently — clicking trigger A does not affect trigger B. Use the data-dropdown attribute approach from the click-outside script to track which instance is currently open.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation