All-in-one Analytics SDK.
Define your analytics events in a YAML schema and get a fully typed TypeScript tracker without typos in event names or missing properties. alyt sends events to Google Analytics, PostHog, Mixpanel, Amplitude, Plausible, and Vercel Analytics through one API. You can 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. alyt skips plugins that don't
implement a method, such as Plausible's missing identify method.
| 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. This is useful for testing and server-side rendering.
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.
Parameters support a short form (name: type) and a full form for metadata:
events:
magic_link_requested:
params:
email:
type: string
hash: truehash: true is only valid for type: string. The generated tracker method
still accepts the raw string argument, but emits the same parameter key with the
exact input string replaced by a lowercase SHA-256 hex digest from
globalThis.crypto.subtle. Alyt does not trim, lowercase, salt, or rename
hashed values. Generated methods with hashed parameters are async; generated
methods without hashed parameters remain synchronous.
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. Use this
when you've already initialized the SDK elsewhere or when 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 |
Start a GitHub Discussion with your proposal and wait for a Mathematic maintainer to review it. If we decide to implement the change, a maintainer or agent will open the pull request.
When Mathematic implements a proposal, it will link the implementation pull request to the Discussion and credit the proposal's original author.
GitHub restricts pull request creation to Mathematic maintainers, repository collaborators with write, maintain, or admin access, and authorized maintenance agents. Read CONTRIBUTING.md for the full process.
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.