Marketing & Social

Free News Ticker Widget

Auto-cycling one-line headlines bar that pauses on hover.

A compact single-line bar that rotates through your headlines with a smooth vertical cycle and an optional link on each item. It is built for blogs, news sites, and stores that want to surface updates without taking up space. Install it with a single script tag wherever you want the bar to appear.

Live demo

Embed this widget

Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.

Hosted (recommended)html
<script src="https://hayneslab.dev/widgets/news-ticker.js" data-hl-config='[{"text":"Spring sale: 25% off all plans ends Friday","href":"#"},{"text":"We just launched our new client portal","href":"#"},{"text":"Free website audits for Charlotte small businesses this month","href":"#"},{"text":"New on the blog: 5 widgets that boost conversions","href":"#"},{"text":"Now booking website redesigns for Q3","href":"#"}]' data-hl-interval="4000" async></script>
<!-- Free News Ticker widget by haynes lab — https://hayneslab.dev/tools/widgets/news-ticker -->
Inline (self-contained)html
<!-- Free News Ticker widget by haynes lab — https://hayneslab.dev/tools/widgets/news-ticker -->
<script data-hl-config='[{"text":"Spring sale: 25% off all plans ends Friday","href":"#"},{"text":"We just launched our new client portal","href":"#"},{"text":"Free website audits for Charlotte small businesses this month","href":"#"},{"text":"New on the blog: 5 widgets that boost conversions","href":"#"},{"text":"Now booking website redesigns for Q3","href":"#"}]' data-hl-interval="4000">
/*!
 * haynes lab — News Ticker widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/news-ticker
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-news-ticker';
  var STYLE_ID = 'hlw-news-ticker-styles';

  function findScript() {
    return document.currentScript ||
      document.querySelector('script[src*="widgets/news-ticker.js"]');
  }

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; display: flex; align-items: center; gap: 12px;' +
      ' max-width: 960px; margin: 0 auto; padding: 10px 16px; overflow: hidden;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 14px; line-height: 1.4; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-radius: 10px; }' +
      '.' + PREFIX + '__label { flex: 0 0 auto; padding: 3px 10px; border-radius: 6px;' +
      ' font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;' +
      ' color: #ffffff; background: var(--hlw-accent); }' +
      '.' + PREFIX + '__viewport { position: relative; flex: 1 1 auto; min-width: 0; height: 1.5em;' +
      ' overflow: hidden; }' +
      '.' + PREFIX + '__track { transition: transform 0.5s ease; }' +
      '.' + PREFIX + '__item { display: block; height: 1.5em; overflow: hidden;' +
      ' white-space: nowrap; text-overflow: ellipsis; color: #374151; text-decoration: none; }' +
      'a.' + PREFIX + '__item:hover { color: var(--hlw-accent); text-decoration: underline; }' +
      'a.' + PREFIX + '__item:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px;' +
      ' border-radius: 3px; }' +
      '.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 2px; font-size: 11px;' +
      ' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none;' +
      ' opacity: 0; transition: opacity 0.2s ease; }' +
      '.' + PREFIX + ':hover .' + PREFIX + '__credit { opacity: 1; }' +
      '.' + PREFIX + '__credit svg { display: block; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
      '@media (max-width: 560px) { .' + PREFIX + ' { gap: 8px; padding: 8px 12px; font-size: 13px; }' +
      ' .' + PREFIX + '__label { padding: 2px 8px; font-size: 10px; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function parseHeadlines(script) {
    var raw = script.getAttribute('data-hl-config');
    if (!raw) {
      console.warn('[haynes lab news-ticker] Missing required data-hl-config attribute (JSON array of {text, href}). Nothing rendered.');
      return null;
    }
    var items;
    try {
      items = JSON.parse(raw);
    } catch (e) {
      console.warn('[haynes lab news-ticker] data-hl-config is not valid JSON. Nothing rendered.', e);
      return null;
    }
    if (!Array.isArray(items)) {
      console.warn('[haynes lab news-ticker] data-hl-config must be a JSON array. Nothing rendered.');
      return null;
    }
    var valid = items.filter(function (t) {
      return t && typeof t.text === 'string' && t.text;
    });
    if (!valid.length) {
      console.warn('[haynes lab news-ticker] data-hl-config has no valid headlines (each needs at least "text"). Nothing rendered.');
      return null;
    }
    return valid;
  }

  function el(tag, className, text) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (text != null) node.textContent = text;
    return node;
  }

  function logoSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 32 32');
    svg.setAttribute('width', '13');
    svg.setAttribute('height', '13');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    function circle(cx, cy, r, attrs) {
      var c = document.createElementNS(svgNS, 'circle');
      c.setAttribute('cx', cx);
      c.setAttribute('cy', cy);
      c.setAttribute('r', r);
      for (var key in attrs) c.setAttribute(key, attrs[key]);
      svg.appendChild(c);
    }
    circle(16, 16, 16, { fill: '#0f0a0a' });
    circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
    circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
    circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
    circle(16, 16, 3, { fill: '#06b6d4' });
    circle(24.5, 7.5, 2, { fill: '#f97316' });
    circle(7, 16, 1.8, { fill: '#f97316' });
    circle(20, 20, 1.5, { fill: '#f97316' });
    return svg;
  }

  function build(script) {
    var items = parseHeadlines(script);
    if (!items) return;

    var interval = parseInt(script.getAttribute('data-hl-interval'), 10);
    if (isNaN(interval) || interval < 2000) interval = 5000;
    var label = script.getAttribute('data-hl-label') || 'LATEST';
    var accent = script.getAttribute('data-hl-accent') || '#f97316';

    injectStyles();

    var root = el('div', PREFIX);
    root.setAttribute('role', 'region');
    root.setAttribute('aria-label', 'Latest headlines');
    root.style.setProperty('--hlw-accent', accent);

    root.appendChild(el('span', PREFIX + '__label', label));

    var viewport = el('div', PREFIX + '__viewport');
    var track = el('div', PREFIX + '__track');
    viewport.appendChild(track);
    root.appendChild(viewport);

    items.forEach(function (t) {
      var href = typeof t.href === 'string' && t.href ? t.href : null;
      var item = el(href ? 'a' : 'span', PREFIX + '__item', t.text);
      if (href) item.href = href;
      track.appendChild(item);
    });

    var index = 0;
    var timer = null;

    function go(i) {
      index = (i + items.length) % items.length;
      track.style.transform = 'translateY(-' + index * 1.5 + 'em)';
    }

    function stop() {
      if (timer) { clearInterval(timer); timer = null; }
    }
    function start() {
      if (items.length > 1 && !timer) {
        timer = setInterval(function () { go(index + 1); }, interval);
      }
    }

    root.addEventListener('mouseenter', stop);
    root.addEventListener('mouseleave', start);
    root.addEventListener('focusin', stop);
    root.addEventListener('focusout', start);
    start();

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/news-ticker';
    credit.target = '_blank';
    credit.rel = 'noopener';
    root.appendChild(credit);

    if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
      script.insertAdjacentElement('afterend', root);
    } else {
      document.body.appendChild(root);
    }
  }

  function init() {
    var script = findScript();
    if (!script) {
      console.warn('[haynes lab news-ticker] Could not locate its own script tag. Nothing rendered.');
      return;
    }
    build(script);
  }

  if (document.body) {
    init();
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();

</script>

Configuration

Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.

AttributeDefaultDescription
data-hl-config(required)JSON array of headlines, each with "text" and an optional "href" link.
data-hl-interval"5000"Milliseconds each headline stays visible before the next one cycles in.
data-hl-label"LATEST"The badge text shown at the left of the bar.
data-hl-accent"#f97316"Color of the label badge, hover states, and focus outlines.

Frequently asked questions

How do I install the News Ticker?

Paste the script tag exactly where you want the bar to appear on your page — it renders inline at that spot. Put your headlines in the data-hl-config attribute as a JSON array of {"text","href"} objects (href is optional).

Can I change the speed, label, and colors?

Yes — data-hl-interval sets how many milliseconds each headline shows, data-hl-label changes the badge text (default "LATEST"), and data-hl-accent changes the badge and hover color. The ticker pauses automatically when a visitor hovers or focuses it.

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single vanilla JavaScript file with no dependencies. Paste the snippet into any platform that lets you add custom scripts or edit your theme, including WordPress, Shopify, Squarespace, Wix, and plain HTML sites.

More free widgets

Need a custom widget?

We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.

Get in Touch