Skip to main content
The primary way to install Grain. Perfect for any website — landing pages, static sites, CMSs, or full web apps. No build tools required.
Using React or Next.js? Try React Quick Start or Next.js Quick Start for hooks-based integration.

Add the Script Tag

Add this to your HTML <head>:
<script src="https://tag.grainql.com/v4/your-tenant-id.js"></script>
<script>
  // SDK auto-initializes, get the instance
  const grain = GrainTag.getInstance();

  // Track custom events
  grain.track('button_clicked', {
    button_id: 'signup'
  });
</script>
Replace your-tenant-id with your tenant identifier (not UUID) from your dashboard. That’s it! Grain is now loaded and tracking.

Complete HTML Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My Landing Page</title>

  <!-- Grain Tag (auto-initializes) -->
  <script src="https://tag.grainql.com/v4/your-tenant-id.js"></script>
</head>
<body>
  <h1>Welcome!</h1>
  <button id="signup-btn">Get Started</button>

  <script>
    // Get SDK instance (already initialized)
    const grain = GrainTag.getInstance();

    // Track button clicks
    document.getElementById('signup-btn').addEventListener('click', function() {
      grain.track('signup_clicked', {
        location: 'hero'
      });

      // Your signup logic...
      window.location.href = '/signup';
    });
  </script>
</body>
</html>

Track Form Submissions

<form id="contact-form">
  <input type="text" name="name" required>
  <input type="email" name="email" required>
  <button type="submit">Submit</button>
</form>

<script>
  document.getElementById('contact-form').addEventListener('submit', function(e) {
    e.preventDefault();

    // Track the submission
    grain.track('form_submitted', {
      form_name: 'contact',
      page: window.location.pathname
    });

    // Your form submission logic...
  });
</script>
<a href="/pricing" id="pricing-link">View Pricing</a>

<script>
  document.getElementById('pricing-link').addEventListener('click', function() {
    grain.track('link_clicked', {
      link_text: 'View Pricing',
      destination: '/pricing'
    });
  });
</script>

Use Remote Configuration

Control your page content without redeploying:
<div id="promo-banner" style="display: none;">
  <p id="promo-text">Special offer!</p>
</div>

<script>
  document.addEventListener('DOMContentLoaded', function() {
    // Check if banner should show
    const showBanner = grain.getConfig('show_promo_banner');
    const bannerText = grain.getConfig('promo_banner_text');

    if (showBanner === 'true') {
      document.getElementById('promo-banner').style.display = 'block';
      document.getElementById('promo-text').textContent = bannerText || 'Special offer!';
    }
  });
</script>
Now you can toggle the banner on/off from your dashboard without touching your code!

Identify Users

Track logged-in users:
<script>
  // When user logs in — pass only the user ID
  function handleLogin(userId) {
    grain.identify(userId);

    // Track login event
    grain.track('user_logged_in', {
      method: 'email'
    });
  }
</script>

Track Page Views on Navigation

For multi-page sites, the script automatically tracks each page load. For single-page apps:
<script>
  // Track browser back/forward
  window.addEventListener('popstate', function() {
    grain.track('page_viewed', {
      page: window.location.pathname
    });
  });

  // Track custom navigation
  function navigateTo(url) {
    history.pushState(null, '', url);

    grain.track('page_viewed', {
      page: url
    });

    // Your navigation logic...
  }
</script>

Performance Tips

Do: Place Grain in the <head> for early initialization
Do: Use one script tag per page — Grain handles everything
Don’t: Load Grain multiple times — once per page is enough

What’s Next?

Using Google Tag Manager instead? Check out the GTM Integration Guide for a no-code setup.