
fluid-marquee is a Vanilla JavaScript marquee library that creates responsive horizontal and vertical scrolling content from simple HTML markup.
You can use the library to build a JavaScript marquee scroller for news tickers, logo strips, announcement bars, product ribbons, and compact content feeds.
Features:
- Scrolls only when the content overflows the container.
- Detects overflow before starting animation.
- Horizontal and vertical scrolling.
- Reverses direction through negative speed values.
- Hover pause and click pause behavior.
- Mouse and touch dragging.
- Momentum after drag release.
- Recalculates layout after resize and image load.
- Pauses motion outside the viewport.
- Runs constant scrolling on the compositor thread.
- JavaScript controls for dynamic items.
How to use it:
1. Install fluid-marquee with NPM and import the CSS and JavaScript in your application entry point:
# NPM $ npm install fluid-marquee
import "fluid-marquee/styles.css"; import "fluid-marquee";
2. Or load the required files directly in your HTML document.
<!-- Local --> <link rel="stylesheet" href="./dist/fluid-marquee.min.css"> <script src="./dist/fluid-marquee.min.js"></script> <!-- Or from a CDN --> <script src=" https://cdn.jsdelivr.net/npm/fluid-marquee/dist/fluid-marquee.min.js "></script> <link href=" https://cdn.jsdelivr.net/npm/fluid-marquee/dist/fluid-marquee.min.css " rel="stylesheet">
3. Place any content you want to scroll inside a container with the class fluid-marquee. Each item needs the class fluid-marquee-item. The library auto-initializes every .fluid-marquee on the page as soon as the DOM is ready. No JavaScript call is required.
<div class="fluid-marquee" aria-label="Featured frontend tools"> <div class="fluid-marquee-item">Canvas Inspector</div> <div class="fluid-marquee-item">CSS Grid Helper</div> <div class="fluid-marquee-item">UI Token Builder</div> <div class="fluid-marquee-item">Form State Debugger</div> </div>
4. Product ribbons often need different motion speeds for different page sections. A positive speed moves in the default direction, and a negative speed reverses the track.
<div class="fluid-marquee" data-fluid-marquee-speed="72"> <div class="fluid-marquee-item">Starter Plan</div> <div class="fluid-marquee-item">Team Plan</div> <div class="fluid-marquee-item">Agency Plan</div> </div> <div class="fluid-marquee" data-fluid-marquee-speed="-120"> <div class="fluid-marquee-item">Free Updates</div> <div class="fluid-marquee-item">MIT License</div> <div class="fluid-marquee-item">No jQuery Required</div> </div>
5. Vertical scrollers fit compact announcement cards and sidebar update panels. The container needs a height because the library measures vertical overflow.
<div class="fluid-marquee" data-fluid-marquee-vertical data-fluid-marquee-speed="48" style="height: 260px;" > <div class="fluid-marquee-item">Build queue finished</div> <div class="fluid-marquee-item">Threeed review</div> <div class="fluid-marquee-item">New customer signup received</div> </div>
6. Interactive marquees need a clear pause model for links, buttons, and readable content. The pausable attribute combines hover pause with click-locked pause.
<div class="fluid-marquee" data-fluid-marquee-pausable> <a class="fluid-marquee-item" href="/tools/chart-builder">Chart Builder</a> <a class="fluid-marquee-item" href="/tools/icon-browser">Icon Browser</a> <a class="fluid-marquee-item" href="/tools/color-checker">Color Checker</a> </div>
7. Client-rendered pages may add the marquee after an API request. Initialize the new container after the DOM node exists.
// Create the marquee after async data arrives.
const updatesPanel = document.querySelector("#release-updates");
updatesPanel.innerHTML = `
<div class="fluid-marquee" data-fluid-marquee-speed="80">
<div class="fluid-marquee-item">v2.1.0 released</div>
<div class="fluid-marquee-item">New theme tokens added</div>
<div class="fluid-marquee-item">Keyboard fixes shipped</div>
</div>
`;
// Initialize all uninitialized marquees inside this panel.
FluidMarquee.initAll(updatesPanel);Configuration Options:
All options are available as data- attributes on the HTML element or as camelCase keys in the JavaScript options object passed to FluidMarquee.init().
speed(number): Scroll speed in pixels per second. Defaults to64. Negative values scroll in reverse.infinite(boolean): Force scrolling at all times, even when content fits inside the container.vertical(boolean): Scroll vertically. The container must have an explicit height.pausable(boolean): Pause on hover and lock-pause on click. Sets bothpauseHoverandpauseClickat once.pauseHover(boolean): Pause while the pointer is over the marquee. Resumes automatically on pointer leave.pauseClick(boolean): Lock-pause on click inside the marquee. Click outside to resume.draggable(boolean): Accept mouse and touch drag to scrub through the marquee. Applies momentum on release.runScripts(boolean): Re-execute<script>tags inside cloned items. Off by default.
API Methods:
// Get the instance from the element after auto-init
const m = document.querySelector(".fluid-marquee").marquee;
// Sticky pause - only resume() clears this state
m.pause();
// User-style pause - clicking outside the marquee also clears this
m.pause(false);
// Clear api and click pauses (hover and drag self-resolve)
m.resume();
// Force a re-measure (ResizeObserver handles most cases automatically)
m.refresh();
// Tear down the marquee and restore items as direct children of the container
m.destroy();
// Append one item
m.add(itemEl);
// Append several items at once
m.add(itemA, itemB, itemC);
// Remove one or more items
m.remove(itemEl);
// Replace all items at once
m.setItems([newItemA, newItemB, newItemC]);
// Read the current item elements as an array
console.log(m.items);
// Read pause state flags
console.log(m.paused); // True if anything keeps the marquee paused
console.log(m.apiPaused); // True if paused via m.pause()
console.log(m.userPaused); // True if hoverPaused || clickPaused || dragPaused
console.log(m.hoverPaused); // True while the pointer hovers
console.log(m.clickPaused); // True when locked via click-pause
console.log(m.dragPaused); // True while the user drags
// Initialize a specific element (walks up with closest, idempotent)
FluidMarquee.init(el, { speed: 80, pausable: true });
// Initialize all uninitialised marquees within a root element
FluidMarquee.initAll(document, { draggable: true });
// Get the instance for an element or any of its descendants
FluidMarquee.get(el);Events.
const el = document.querySelector(".fluid-marquee");
// Fires on the element once it finishes initialising
el.addEventListener("fluid-marquee:init", function (e) {
console.log("Initialised:", e.target.marquee);
});
// Fires on window once the initial auto-init pass completes after DOMContentLoaded.
// Use this when your script runs before the library has had a chance to initialize.
addEventListener("fluid-marquee:ready", function () {
document.querySelector(".fluid-marquee").marquee.pause();
});
// Fires when a pause cause activates, as long as no higher-priority cause is already active.
// e.detail.cause is one of: "api" | "click" | "drag" | "hover"
// Priority order: api > click > drag > hover
el.addEventListener("fluid-marquee:pause", function (e) {
if (e.detail.cause === "hover") return; // ignore hover-only pauses
document.getElementById("status").textContent = "Paused";
});
// Fires when a pause cause deactivates
el.addEventListener("fluid-marquee:resume", function (e) {
if (e.detail.cause === "hover") return;
document.getElementById("status").textContent = "Playing";
});Alternatives:
- Discover more free scroller libraries In JavaScript & CSS
- Smooth, Infinite Scrolling Marquees in Vanilla JavaScript
- Dynamic Marquee-like Text Scroller In Vanilla JavaScript
- Simple Marquee Like Content Scrolling In Vanilla JavaScript
- Vanilla JavaScript Marquee Library
- Responsive Text Scrolling Effect – Pure CSS Marquee
- Create A Simple News Ticker using Pure CSS / CSS3
FAQs:
Q: Why is my marquee not scrolling?
A: The content may fit inside the container. fluid-marquee stays static by default in that case. Add data-fluid-marquee-infinite if you want motion at all widths.
Q: Can I use fluid-marquee with React or Vue?
A: Yes. Render the marquee container after the component mounts, then initialize it from JavaScript. Use the item API for dynamic changes instead of replacing the children through reactive rendering.
Q: Does fluid-marquee require jQuery?
A: No. It uses modern JavaScript, CSS, ResizeObserver, IntersectionObserver, Web Animations API, and requestAnimationFrame.
Q: Why do my item styles stop working after initialization?
A: The library wraps your original children inside internal elements. Replace direct-child selectors with .fluid-marquee-item or a descendant selector.
Q: How should I handle buttons or links inside cloned marquee items?
A: Use event delegation on the marquee container. A single listener can handle clicks from the original items and their cloned copies.






