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'
});
Consent Management
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