Skip to main content
This guide uses the Grain Tag script, which works with Vue, Svelte, Angular, or plain JavaScript. Using React? Try the React Quick Start for a better experience.

Add the Grain Tag

Add the Grain Tag script to your HTML:
<script src="https://tag.grainql.com/v4/your-tenant-id.js"></script>
Replace your-tenant-id with your tenant identifier from your dashboard. Then get the SDK instance in your JavaScript:
const grain = GrainTag.getInstance();
Need npm module imports? Install @grainql/tag via npm for use with bundlers. See the Installation guide for details.

Track Events

Call track() with an event name and optional properties:
// Track a button click
grain.track('button_clicked', {
  button_name: 'signup',
  location: 'hero'
});

// Track a page view
grain.track('page_viewed', {
  page: window.location.pathname,
  title: document.title
});

// Track a form submission
grain.track('form_submitted', {
  form_name: 'contact'
});
Events are automatically batched and sent. You don’t need to manually flush.

Use Remote Config

Get configuration values instantly:
// Get a config value (returns from cache or default)
const heroText = grain.getConfig('hero_text');
console.log(heroText); // "Welcome!"

// Check if a feature is enabled
const isNewUIEnabled = grain.getConfig('new_ui_enabled');
if (isNewUIEnabled === 'true') {
  showNewUI();
} else {
  showOldUI();
}
Cache-first loading: getConfig() returns immediately with cached or default values. Fresh values load in the background and update automatically.

Listen for Config Updates

Subscribe to changes when remote values load:
grain.onConfigsUpdated((configs) => {
  console.log('Configs updated!', configs);

  // Update your UI with the new values
  const heroText = grain.getConfig('hero_text');
  document.querySelector('h1').textContent = heroText;
});

Identify Users

Associate events with a specific user:
// When a user logs in — pass the user ID
grain.identify('user_123');

// All subsequent events include this user ID
grain.track('feature_used', {
  feature_name: 'export'
});
Grain is cookieless by default. When you need explicit consent handling:
// Grant consent (enables persistent tracking)
grain.consent.grant();

// Revoke consent
grain.consent.revoke();

// Check consent status
const status = grain.consent.status();

Complete Example

Here’s a small app that puts it all together:
<!DOCTYPE html>
<html lang="en">
<head>
  <script src="https://tag.grainql.com/v4/your-tenant-id.js"></script>
</head>
<body>
  <div id="hero">
    <h1>Welcome!</h1>
  </div>
  <div id="banner" style="display: none;">
    <p>Special offer!</p>
  </div>
  <button id="signup-btn">Get Started</button>

  <script>
    const grain = GrainTag.getInstance();

    // Track page view on load
    grain.track('page_viewed', {
      page: window.location.pathname,
      title: document.title
    });

    // Use remote config to control UI
    const heroText = grain.getConfig('hero_text');
    if (heroText) {
      document.querySelector('#hero h1').textContent = heroText;
    }

    const showBanner = grain.getConfig('show_banner');
    if (showBanner === 'true') {
      document.querySelector('#banner').style.display = 'block';
    }

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

    // Update UI when fresh config loads
    grain.onConfigsUpdated(() => {
      const newHeroText = grain.getConfig('hero_text');
      if (newHeroText) {
        document.querySelector('#hero h1').textContent = newHeroText;
      }
    });

    // Identify user after login
    async function handleLogin(email, password) {
      const user = await loginUser(email, password);
      grain.identify(user.id);
    }
  </script>
</body>
</html>

Vue.js Example

Using Grain in a Vue component:
<template>
  <div>
    <h1>{{ heroText }}</h1>
    <button @click="handleClick">Get Started</button>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const heroText = ref('Welcome!');

onMounted(() => {
  const grain = GrainTag.getInstance();

  // Get initial config value
  heroText.value = grain.getConfig('hero_text') || 'Welcome!';

  // Update when remote config loads
  grain.onConfigsUpdated(() => {
    heroText.value = grain.getConfig('hero_text') || 'Welcome!';
  });
});

const handleClick = () => {
  const grain = GrainTag.getInstance();
  grain.track('button_clicked', { location: 'hero' });
};
</script>

Svelte Example

Using Grain in a Svelte component:
<script>
  import { onMount } from 'svelte';

  let heroText = 'Welcome!';

  onMount(() => {
    const grain = GrainTag.getInstance();

    // Get initial config value
    heroText = grain.getConfig('hero_text') || 'Welcome!';

    // Update when remote config loads
    grain.onConfigsUpdated(() => {
      heroText = grain.getConfig('hero_text') || 'Welcome!';
    });
  });

  function handleClick() {
    const grain = GrainTag.getInstance();
    grain.track('button_clicked', { location: 'hero' });
  }
</script>

<div>
  <h1>{heroText}</h1>
  <button on:click={handleClick}>Get Started</button>
</div>

Track Page Views Automatically

For single-page apps, track route changes:
const grain = GrainTag.getInstance();

// For browser history API
window.addEventListener('popstate', () => {
  grain.track('page_viewed', {
    page: window.location.pathname
  });
});

// For Vue Router
router.afterEach((to) => {
  grain.track('page_viewed', {
    page: to.path,
    name: to.name
  });
});

What’s Next?

Core API Reference

See all available methods and options

User Identification

Learn about tracking users across sessions

Trackers

Track element clicks without writing code

Event Best Practices

Learn what to track and how to structure events