The primary way to install Grain. Perfect for any website — landing pages, static sites, CMSs, or full web apps. No build tools required.
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 >
< 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 >
Track Link Clicks
< 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 >
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?
Core API Reference See all available methods and options
Event Tracking Guide Learn what to track and best practices
Remote Config Guide Master configuration and feature flags
Trackers Track element clicks without code