Marketing & Social

Free Team Member Grid Widget

Showcase your team with photos, roles, bios, and LinkedIn links in a clean grid.

The Team Member Grid widget renders responsive team cards with a photo, name, role, optional bio, and optional LinkedIn link from a simple JSON config. It is perfect for about pages, agency sites, and small business "meet the team" sections. Install it by pasting one script tag wherever you want the grid 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/team-member-grid.js" data-hl-config='[{"name":"Ava Carter","role":"Founder & CEO","photo":"https://placehold.co/200x200/f97316/ffffff?text=AC","bio":"Leads strategy and keeps the coffee coming.","linkedin":"https://www.linkedin.com/"},{"name":"Marcus Lee","role":"Lead Developer","photo":"https://placehold.co/200x200/06b6d4/ffffff?text=ML","bio":"Turns coffee into clean, shippable code.","linkedin":"https://www.linkedin.com/"},{"name":"Sofia Reyes","role":"Design Director","photo":"https://placehold.co/200x200/0f172a/ffffff?text=SR","bio":"Obsessed with pixels, grids, and good type."}]' data-hl-title="Meet the Team" async></script>
<!-- Free Team Member Grid widget by haynes lab — https://hayneslab.dev/tools/widgets/team-member-grid -->
Inline (self-contained)html
<!-- Free Team Member Grid widget by haynes lab — https://hayneslab.dev/tools/widgets/team-member-grid -->
<script data-hl-config='[{"name":"Ava Carter","role":"Founder & CEO","photo":"https://placehold.co/200x200/f97316/ffffff?text=AC","bio":"Leads strategy and keeps the coffee coming.","linkedin":"https://www.linkedin.com/"},{"name":"Marcus Lee","role":"Lead Developer","photo":"https://placehold.co/200x200/06b6d4/ffffff?text=ML","bio":"Turns coffee into clean, shippable code.","linkedin":"https://www.linkedin.com/"},{"name":"Sofia Reyes","role":"Design Director","photo":"https://placehold.co/200x200/0f172a/ffffff?text=SR","bio":"Obsessed with pixels, grids, and good type."}]' data-hl-title="Meet the Team">
/*!
 * haynes lab — Team Member Grid widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/team-member-grid
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-team-member-grid';
  var STYLE_ID = 'hlw-team-member-grid-styles';

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

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; max-width: 960px; margin: 0 auto; padding: 24px 24px 40px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 16px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-radius: 12px; }' +
      '.' + PREFIX + '__title { margin: 0 0 20px; font-size: 22px; font-weight: 700;' +
      ' color: #111827; text-align: center; }' +
      '.' + PREFIX + '__grid { display: grid; gap: 20px; }' +
      '.' + PREFIX + '__card { border: 1px solid #e5e7eb; border-radius: 10px; padding: 24px 16px 20px;' +
      ' background: #f9fafb; text-align: center; display: flex; flex-direction: column; align-items: center; }' +
      '.' + PREFIX + '__photo { width: 96px; height: 96px; border-radius: 50%; object-fit: cover;' +
      ' border: 2px solid #e5e7eb; margin-bottom: 12px; display: block; }' +
      '.' + PREFIX + '__photo--fallback { display: flex; align-items: center; justify-content: center;' +
      ' background: #e5e7eb; color: #6b7280; font-size: 32px; font-weight: 700; }' +
      '.' + PREFIX + '__name { font-weight: 600; font-size: 16px; color: #111827; }' +
      '.' + PREFIX + '__role { font-size: 13px; color: #6b7280; margin-top: 2px; }' +
      '.' + PREFIX + '__bio { margin: 10px 0 0; font-size: 14px; color: #374151; }' +
      '.' + PREFIX + '__linkedin { margin-top: 12px; width: 34px; height: 34px; border-radius: 50%;' +
      ' border: 1px solid #e5e7eb; background: #ffffff; color: #0a66c2; display: inline-flex;' +
      ' align-items: center; justify-content: center; text-decoration: none; }' +
      '.' + PREFIX + '__linkedin:hover { border-color: #0a66c2; }' +
      '.' + PREFIX + '__linkedin:focus-visible { outline: 2px solid #0a66c2; outline-offset: 2px; }' +
      '.' + PREFIX + '__linkedin svg { display: block; }' +
      '.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 6px; font-size: 11px;' +
      ' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }' +
      '.' + PREFIX + '__credit svg { display: block; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
      '@media (max-width: 900px) { .' + PREFIX + '__grid { grid-template-columns: repeat(2, 1fr) !important; } }' +
      '@media (max-width: 560px) { .' + PREFIX + '__grid { grid-template-columns: 1fr !important; }' +
      ' .' + PREFIX + ' { padding: 20px 16px 38px; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function parseMembers(script) {
    var raw = script.getAttribute('data-hl-config');
    if (!raw) {
      console.warn('[haynes lab team-member-grid] Missing required data-hl-config attribute (JSON array of {name, role, photo, bio, linkedin}). Nothing rendered.');
      return null;
    }
    var items;
    try {
      items = JSON.parse(raw);
    } catch (e) {
      console.warn('[haynes lab team-member-grid] data-hl-config is not valid JSON. Nothing rendered.', e);
      return null;
    }
    if (!Array.isArray(items)) {
      console.warn('[haynes lab team-member-grid] data-hl-config must be a JSON array. Nothing rendered.');
      return null;
    }
    var valid = items.filter(function (m) {
      return m && typeof m.name === 'string' && m.name;
    });
    if (!valid.length) {
      console.warn('[haynes lab team-member-grid] data-hl-config has no valid members (each needs at least "name"). 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 linkedinSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('width', '16');
    svg.setAttribute('height', '16');
    svg.setAttribute('fill', 'currentColor');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    var path = document.createElementNS(svgNS, 'path');
    path.setAttribute('d', 'M20.45 20.45h-3.55v-5.57c0-1.33-.03-3.04-1.85-3.04-1.86 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56v11.45z');
    svg.appendChild(path);
    return svg;
  }

  function safeUrl(url) {
    if (typeof url !== 'string' || !url) return null;
    if (url.indexOf('https://') === 0 || url.indexOf('http://') === 0) return url;
    return null;
  }

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

    var columns = parseInt(script.getAttribute('data-hl-columns'), 10);
    if (isNaN(columns) || columns < 1) columns = 3;
    if (columns > 6) columns = 6;
    var title = script.getAttribute('data-hl-title');

    injectStyles();

    var root = el('div', PREFIX);
    root.setAttribute('role', 'region');
    root.setAttribute('aria-label', title || 'Team members');

    if (title) root.appendChild(el('h2', PREFIX + '__title', title));

    var grid = el('div', PREFIX + '__grid');
    grid.style.gridTemplateColumns = 'repeat(' + columns + ', 1fr)';
    root.appendChild(grid);

    items.forEach(function (m) {
      var card = el('div', PREFIX + '__card');

      var photo = safeUrl(m.photo);
      if (photo) {
        var img = el('img', PREFIX + '__photo');
        img.src = photo;
        img.alt = m.name;
        img.loading = 'lazy';
        card.appendChild(img);
      } else {
        var fallback = el('div', PREFIX + '__photo ' + PREFIX + '__photo--fallback', m.name.charAt(0).toUpperCase());
        fallback.setAttribute('aria-hidden', 'true');
        card.appendChild(fallback);
      }

      card.appendChild(el('div', PREFIX + '__name', m.name));
      if (m.role) card.appendChild(el('div', PREFIX + '__role', String(m.role)));
      if (m.bio) card.appendChild(el('p', PREFIX + '__bio', String(m.bio)));

      var linkedin = safeUrl(m.linkedin);
      if (linkedin) {
        var link = el('a', PREFIX + '__linkedin');
        link.href = linkedin;
        link.target = '_blank';
        link.rel = 'noopener';
        link.setAttribute('aria-label', m.name + ' on LinkedIn');
        link.appendChild(linkedinSvg());
        card.appendChild(link);
      }

      grid.appendChild(card);
    });

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/team-member-grid';
    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 team-member-grid] 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 members, each with "name" (required) and optional "role", "photo", "bio", and "linkedin" URL.
data-hl-columns"3"Number of grid columns on desktop (1-6); collapses to 2 on tablets and 1 on phones.
data-hl-title(none)Optional heading shown above the grid, e.g. "Meet the Team".

Frequently asked questions

How do I install the Team Member Grid?

Paste the script tag snippet into your page where you want the grid to appear. The widget renders inline right after the script tag, using the members you define in the data-hl-config attribute.

How do I customize the members and layout?

Edit the JSON in data-hl-config — each member can have a name, role, photo URL, short bio, and LinkedIn URL. Set data-hl-columns (1-6) to change the desktop column count and data-hl-title to add a heading above the grid.

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single dependency-free vanilla JavaScript file. Paste the script tag into any HTML block, custom code section, or theme template and it works anywhere scripts are allowed.

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