All-in-one Analytics SDK.
Define your analytics events in a YAML schema and get a fully typed TypeScript tracker — no more typos in event names or missing properties. alyt sends events to Google Analytics, PostHog, Mixpanel, Amplitude, Plausible, and Vercel Analytics through a single API, and lets you add or remove providers at runtime for consent flows.
# core
pnpm add @alyt/core
# plugins (pick what you use)
pnpm add @alyt/plugin-ga
pnpm add @alyt/plugin-posthog
pnpm add @alyt/plugin-mixpanel
pnpm add @alyt/plugin-amplitude
pnpm add @alyt/plugin-plausible
pnpm add @alyt/plugin-vercel
# react bindings
pnpm add @alyt/react
# codegen (dev dependency)
pnpm add -D @alyt/codegenimport { createAnalytics } from "@alyt/core";
import { googleAnalytics } from "@alyt/plugin-ga";
import { posthog } from "@alyt/plugin-posthog";
const analytics = createAnalytics({
plugins: [
googleAnalytics({ measurementId: "G-XXXXXXXXXX" }),
posthog({ apiKey: "phc_XXXXXXXXXX" }),
],
});
analytics.track("button_clicked", { label: "Sign Up" });
analytics.identify("user_123", { plan: "pro" });
analytics.page("Home");Every call fans out to all registered plugins. Plugins that don't implement a method (e.g. Plausible has no identify) are silently skipped.
| Plugin | Package | Factory | Required Options |
|---|---|---|---|
| Google Analytics | @alyt/plugin-ga |
googleAnalytics() |
measurementId: string |
| PostHog | @alyt/plugin-posthog |
posthog() |
apiKey: string |
| Mixpanel | @alyt/plugin-mixpanel |
mixpanel() |
token: string |
| Amplitude | @alyt/plugin-amplitude |
amplitude() |
apiKey: string |
| Plausible | @alyt/plugin-plausible |
plausible() |
domain: string |
| Vercel Analytics | @alyt/plugin-vercel |
vercelAnalytics() |
(none) |
Each plugin also accepts an optional client property to inject an SDK instance (useful for testing or SSR).
Plugins that need a <script> tag provide a React component under a /react subpath:
import { GAScript } from "@alyt/plugin-ga/react";
import { VercelAnalytics } from "@alyt/plugin-vercel/react";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<GAScript measurementId="G-XXXXXXXXXX" />
<VercelAnalytics />
{children}
</body>
</html>
);
}| Package | Component | Props |
|---|---|---|
@alyt/plugin-ga/react |
GAScript |
measurementId?: string (falls back to NEXT_PUBLIC_GA_MEASUREMENT_ID) |
@alyt/plugin-posthog/react |
PostHogScript |
apiKey: string, apiHost?: string |
@alyt/plugin-mixpanel/react |
MixpanelScript |
token: string |
@alyt/plugin-amplitude/react |
AmplitudeScript |
apiKey: string |
@alyt/plugin-plausible/react |
PlausibleScript |
domain: string, apiHost?: string |
@alyt/plugin-vercel/react |
VercelAnalytics |
(re-export of @vercel/analytics/react) |
Wrap your app with AnalyticsProvider and use the useAnalytics hook in any component:
import { createAnalytics } from "@alyt/core";
import { AnalyticsProvider, useAnalytics } from "@alyt/react";
import { googleAnalytics } from "@alyt/plugin-ga";
const client = createAnalytics({
plugins: [googleAnalytics({ measurementId: "G-XXXXXXXXXX" })],
});
// In your root layout
function App() {
return (
<AnalyticsProvider client={client}>
<Dashboard />
</AnalyticsProvider>
);
}
// In any component
function Dashboard() {
const analytics = useAnalytics();
return (
<button onClick={() => analytics.track("dashboard_clicked")}>
Go to Dashboard
</button>
);
}useAnalytics() throws if used outside an <AnalyticsProvider>.
Define your events in a YAML schema and generate a fully typed tracker.
# analytics.schema.yaml
events:
page_viewed:
description: Fired when a user views a page
params:
page_name: string
button_clicked:
description: Fired when a user clicks a button
params:
button_id: string
label: string
user_signed_up:
description: Fired when a new user completes registrationEvents with no parameters can omit params. The optional description field generates JSDoc comments in the output.
npx alyt-codegen --schema analytics.schema.yaml --out lib/analyticsThe CLI generates two files:
types.ts -- Event name union and parameter map:
export type AnalyticsEventName =
| "page_viewed"
| "button_clicked"
| "user_signed_up";
export interface AnalyticsEventMap {
/** Fired when a user views a page */
page_viewed: { page_name: string };
/** Fired when a user clicks a button */
button_clicked: { button_id: string; label: string };
/** Fired when a new user completes registration */
user_signed_up: Record<string, never>;
}tracker.ts -- Typed methods that call client.track():
import type { AnalyticsClient, TrackOptions } from "@alyt/core";
export function createTracker(client: AnalyticsClient) {
return {
/** Fired when a user views a page */
pageViewed(pageName: string, options?: TrackOptions) {
client.track("page_viewed", { page_name: pageName }, options);
},
/** Fired when a user clicks a button */
buttonClicked(buttonId: string, label: string, options?: TrackOptions) {
client.track("button_clicked", { button_id: buttonId, label: label }, options);
},
/** Fired when a new user completes registration */
userSignedUp(options?: TrackOptions) {
client.track("user_signed_up", undefined, options);
},
};
}Parameter names are converted from snake_case (YAML) to camelCase (TypeScript). Descriptions become JSDoc comments on both the type map and the tracker methods.
Send an event to specific plugins only using TrackOptions.only:
// Only send to Google Analytics, skip PostHog
analytics.track("internal_metric", { value: 42 }, { only: ["google-analytics"] });Plugin names are the name property returned by each factory:
| Factory | Plugin Name |
|---|---|
googleAnalytics() |
"google-analytics" |
posthog() |
"posthog" |
mixpanel() |
"mixpanel" |
amplitude() |
"amplitude" |
plausible() |
"plausible" |
vercelAnalytics() |
"vercel" |
The generated tracker also accepts TrackOptions as the last argument on every method.
Every plugin accepts a client option to pass your own SDK instance. This is useful when you've already initialized the SDK elsewhere, or for testing:
import { googleAnalytics } from "@alyt/plugin-ga";
// Use your own gtag instance
const plugin = googleAnalytics({
measurementId: "G-XXXXXXXXXX",
client: myCustomGtag,
});Add or remove plugins at runtime -- useful for cookie consent flows:
import { createAnalytics } from "@alyt/core";
import { googleAnalytics } from "@alyt/plugin-ga";
import { posthog } from "@alyt/plugin-posthog";
const analytics = createAnalytics(); // start with no plugins
// User accepts analytics cookies
analytics.addPlugin(googleAnalytics({ measurementId: "G-XXXXXXXXXX" }));
analytics.addPlugin(posthog({ apiKey: "phc_XXXXXXXXXX" }));
// User revokes consent
analytics.removePlugin("google-analytics");
analytics.removePlugin("posthog");Events tracked before any plugins are added are silently dropped.
Implement the AnalyticsPlugin interface to write your own:
import type { AnalyticsPlugin } from "@alyt/core";
export function consoleAnalytics(): AnalyticsPlugin {
return {
name: "console",
track(event, params) {
console.log("[track]", event, params);
},
identify(userId, traits) {
console.log("[identify]", userId, traits);
},
page(name, params) {
console.log("[page]", name, params);
},
reset() {
console.log("[reset]");
},
};
}Only name and track are required. The identify, page, and reset methods are optional.
| Package | Description |
|---|---|
@alyt/core |
createAnalytics client and plugin interface |
@alyt/react |
AnalyticsProvider and useAnalytics hook |
@alyt/codegen |
YAML-to-TypeScript event codegen CLI |
@alyt/plugin-ga |
Google Analytics (gtag.js) |
@alyt/plugin-posthog |
PostHog |
@alyt/plugin-mixpanel |
Mixpanel |
@alyt/plugin-amplitude |
Amplitude |
@alyt/plugin-plausible |
Plausible Analytics |
@alyt/plugin-vercel |
Vercel Analytics |
Apache-2.0
This project is free and open-source work by a 501(c)(3) non-profit. If you find it useful, please consider donating.