Skip to content

Repository files navigation

alyt

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.

Install

# 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/codegen

Quick Start

import { 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.

Plugins

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.

Script Loaders (React / Next.js)

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)

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>.

Codegen

Define your events in a YAML schema and generate a fully typed tracker.

1. Define your schema

# 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 registration

Events 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: true

hash: 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.

2. Run the CLI

npx alyt-codegen --schema analytics.schema.yaml --out lib/analytics

3. Use the generated tracker

The 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.

Selective Tracking

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.

Custom Client

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,
});

Dynamic Plugins

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.

Custom Plugins

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.

Packages

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

Contributing

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.

License

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.

About

All-in-one analytics SDK for TypeScript — unified API for GA, PostHog, Mixpanel, Amplitude, Plausible, and Vercel Analytics with YAML codegen and React bindings

Topics

Resources

Contributing

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages