June.so
June.so: B2B product analytics, company-level insights, feature adoption reports, activation tracking, Node and JavaScript SDK
You are an expert in integrating June.so for B2B product analytics with company-level insights.
## Key Points
- Always call `group()` after `identify()` to link users to companies. June's B2B reports (company-level retention, feature adoption per company) depend on this association.
- Use June's recommended event names (`Signed Up`, `Invite Sent`, `Feature Activated`) to take advantage of auto-generated reports instead of building custom dashboards from scratch.
- Batch server-side events and call `june.flush()` before process exit (e.g., in serverless function teardown) to avoid losing events.
- Omitting the `group()` call means company-level analytics will be empty. This is the most common integration mistake, especially when migrating from a user-only analytics tool.
## Quick Example
```bash
npm install @june-so/analytics-node
```
```html
<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("June snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","screen","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware","registerPlugin"];analytics.factory=function(e){return function(){if(window.analytics.initialized)return window.analytics[e].apply(window.analytics,arguments);var i=Array.prototype.slice.call(arguments);i.unshift(e);analytics.push(i);return analytics}};for(var i=0;i<analytics.methods.length;i++){var key=analytics.methods[i];analytics[key]=analytics.factory(key)}analytics.load=function(key,i){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.src="https://unpkg.com/@june-so/analytics-next/dist/umd/standalone.js";t.addEventListener("load",function(e){window.analytics.load(key);window.analytics.page()});var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._writeKey=key};analytics.load("YOUR_WRITE_KEY")}}();
</script>
```skilldb get analytics-services-skills/June.soFull skill: 223 linesJune.so — Analytics Integration
You are an expert in integrating June.so for B2B product analytics with company-level insights.
Core Philosophy
Overview
June.so is a product analytics platform designed for B2B SaaS companies. It provides out-of-the-box reports for activation, retention, feature adoption, and active users at both the user and company level. June auto-generates reports from standard event data and requires minimal configuration compared to general-purpose analytics tools.
Setup & Configuration
Install the SDK
npm install @june-so/analytics-node
Node.js / Server-Side Setup
import Analytics from "@june-so/analytics-node";
const june = new Analytics("YOUR_WRITE_KEY");
// Flush events on shutdown
process.on("SIGTERM", () => {
june.flush();
});
Browser SDK (Client-Side)
<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("June snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","screen","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware","registerPlugin"];analytics.factory=function(e){return function(){if(window.analytics.initialized)return window.analytics[e].apply(window.analytics,arguments);var i=Array.prototype.slice.call(arguments);i.unshift(e);analytics.push(i);return analytics}};for(var i=0;i<analytics.methods.length;i++){var key=analytics.methods[i];analytics[key]=analytics.factory(key)}analytics.load=function(key,i){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.src="https://unpkg.com/@june-so/analytics-next/dist/umd/standalone.js";t.addEventListener("load",function(e){window.analytics.load(key);window.analytics.page()});var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._writeKey=key};analytics.load("YOUR_WRITE_KEY")}}();
</script>
Next.js App Router Integration
// lib/june.ts
import Analytics from "@june-so/analytics-node";
let juneClient: Analytics | null = null;
export function getJune(): Analytics {
if (!juneClient) {
juneClient = new Analytics(process.env.JUNE_WRITE_KEY!);
}
return juneClient;
}
// app/api/track/route.ts
import { getJune } from "@/lib/june";
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const { userId, event, properties } = await req.json();
const june = getJune();
june.track({
userId,
event,
properties,
});
return NextResponse.json({ ok: true });
}
Core Patterns
Identifying Users
// Identify a user with traits
june.identify({
userId: "user-123",
traits: {
name: "Alice Chen",
email: "alice@acme.com",
createdAt: "2025-01-15T10:00:00Z",
plan: "growth",
role: "admin",
},
});
Company / Group Tracking
June's key differentiator is company-level analytics. Use group to associate users with companies:
// Associate user with a company
june.group({
userId: "user-123",
groupId: "company-acme",
traits: {
name: "Acme Corp",
industry: "SaaS",
plan: "enterprise",
employeeCount: 150,
createdAt: "2024-06-01T00:00:00Z",
mrr: 2400,
},
});
Tracking Events
// Track feature usage
june.track({
userId: "user-123",
event: "Report Generated",
properties: {
reportType: "revenue-forecast",
format: "pdf",
dataRange: "last-30-days",
},
});
// Track activation milestones
june.track({
userId: "user-123",
event: "Activation Milestone",
properties: {
milestone: "first-integration-connected",
daysFromSignup: 2,
},
});
// Track page views
june.page({
userId: "user-123",
name: "Dashboard",
properties: {
section: "analytics",
},
});
Recommended Event Naming for Auto-Reports
June auto-generates reports when you use its expected event conventions:
// Signup event — drives activation reports
june.track({ userId: "user-123", event: "Signed Up" });
// Invite events — drives virality reports
june.track({
userId: "user-123",
event: "Invite Sent",
properties: { inviteeEmail: "bob@acme.com" },
});
// Feature events — drives feature adoption reports
june.track({
userId: "user-123",
event: "Feature Activated",
properties: { feature: "api-access" },
});
Server-Side Middleware Pattern
// Express middleware for automatic route tracking
import { getJune } from "./lib/june";
function juneTrackingMiddleware(req, res, next) {
const start = Date.now();
res.on("finish", () => {
if (req.user) {
getJune().track({
userId: req.user.id,
event: "API Request",
properties: {
method: req.method,
path: req.route?.path || req.path,
statusCode: res.statusCode,
durationMs: Date.now() - start,
},
});
}
});
next();
}
Best Practices
- Always call
group()afteridentify()to link users to companies. June's B2B reports (company-level retention, feature adoption per company) depend on this association. - Use June's recommended event names (
Signed Up,Invite Sent,Feature Activated) to take advantage of auto-generated reports instead of building custom dashboards from scratch. - Batch server-side events and call
june.flush()before process exit (e.g., in serverless function teardown) to avoid losing events.
Common Pitfalls
- Omitting the
group()call means company-level analytics will be empty. This is the most common integration mistake, especially when migrating from a user-only analytics tool. - Sending high-cardinality property values (e.g., full URLs, unique IDs) as event properties pollutes reports. Use normalized categories instead (e.g., route pattern
/users/:idrather than/users/12345).
Anti-Patterns
Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.
Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.
Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.
Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.
Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.
Install this skill directly: skilldb add analytics-services-skills
Related Skills
Amplitude Analytics
"Amplitude: product analytics, behavioral cohorts, funnels, retention, experiment platform, user journeys, JavaScript SDK"
Heap
Heap: autocapture product analytics, session replay, retroactive event definition, funnel and retention analysis, JavaScript SDK
Mixpanel Analytics
"Mixpanel: event-based analytics, funnels, retention, user profiles, cohorts, group analytics, JavaScript/Node SDK"
Plausible Analytics
"Plausible: privacy-friendly analytics, no cookies, lightweight script, custom events, goals, API, self-hosted option"
PostHog Analytics
"PostHog: product analytics, event tracking, feature flags, session recordings, A/B testing, self-hosted/cloud, Next.js SDK"
Segment
Segment: customer data platform, event routing, identity resolution, analytics.js, server-side sources, warehouse destinations