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?