---
title: Umami Analytics
description: Open-source, cookieless analytics with a prebuilt helper that maps
  Umami's data attributes into a c15t-managed script.
group: integrations
icon: umami-analytics
---
Umami is an open-source, cookieless analytics product configured entirely through `data-*` attributes on its loader. The `umamiAnalytics()` helper serializes your website ID, host override, and tracking options into those attributes and hands the result to c15t's script loader.

## Integrate with c15t

**React**

```tsx
import { type ReactNode } from 'react';
import { ConsentManagerProvider } from '@c15t/react';
import { umamiAnalytics } from '@c15t/scripts/umami-analytics';

const scripts = [umamiAnalytics({ websiteId: 'site-abc-123' })];

export function ConsentProvider({ children }: { children: ReactNode }) {
  return (
    <ConsentManagerProvider
      options={{
        mode: 'hosted',
        backendURL: 'https://your-instance.c15t.dev',
        scripts,
      }}
    >
      {children}
    </ConsentManagerProvider>
  );
}
```

**Next.js**

```tsx
'use client';

import { type ReactNode } from 'react';
import { ConsentManagerProvider } from '@c15t/nextjs';
import { umamiAnalytics } from '@c15t/scripts/umami-analytics';

const scripts = [umamiAnalytics({ websiteId: 'site-abc-123' })];

export function ConsentProvider({ children }: { children: ReactNode }) {
  return (
    <ConsentManagerProvider
      options={{
        mode: 'hosted',
        backendURL: '/api/c15t',
        scripts,
      }}
    >
      {children}
    </ConsentManagerProvider>
  );
}
```

**JavaScript**

```ts
import { getOrCreateConsentRuntime } from 'c15t';
import { umamiAnalytics } from '@c15t/scripts/umami-analytics';

getOrCreateConsentRuntime({
  mode: 'hosted',
  backendURL: 'https://your-instance.c15t.dev',
  scripts: [umamiAnalytics({ websiteId: 'site-abc-123' })],
});
```

## How c15t loads it

* **Category:** `measurement` (Analytics)
* **Loads when:** measurement consent is granted
* **On revocation:** unloaded — c15t removes the script element from the DOM. Umami is cookieless, so no client-side state needs clearing.

If you self-host Umami or need to restrict tracking to specific domains:

```ts
umamiAnalytics({
  websiteId: 'site-abc-123',
  hostUrl: 'https://analytics.example.com',
  domains: ['example.com', 'www.example.com'],
  autoTrack: false,
  tag: 'release-2025',
})
```

Each option is mapped to Umami's published `data-*` attribute (`data-website-id`, `data-host-url`, `data-auto-track`, `data-domains`, `data-tag`, `data-before-send`) and the script uses `defer` so it matches Umami's recommended embed pattern.

> 📝 **Note:**
> beforeSend accepts a string referencing a global hook (e.g. 'window\.umamiBeforeSend'), not a function value. The c15t manifest runtime serializes script configuration, so inline callbacks cannot be passed through it.

## Tracking events in your app

c15t gates the Umami script from loading until `measurement` consent is granted. Your application code that calls Umami's runtime API (`window.umami.track`, `window.umami.identify`) is **not** automatically gated — `window.umami` does not exist until the script is loaded, so unguarded calls before consent throw.

Guard event calls by checking consent state. From React:

```tsx
import { useConsentManager } from '@c15t/react';

function useTrackSignup() {
  const { has } = useConsentManager();

  return () => {
    if (has('measurement')) {
      window.umami?.track('signup');
    }
  };
}
```

From plain JavaScript:

```ts
import { getOrCreateConsentRuntime } from 'c15t';

const { consentStore } = getOrCreateConsentRuntime();

if (consentStore.getState().has('measurement')) {
  window.umami?.track('signup');
}
```

## Types

### UmamiAnalyticsOptions

|Property|Value|
|:--|:--|
|Type Name|\`UmamiAnalyticsOptions\`|
|Source Path|\`./packages/scripts/src/vendors/analytics/umami-analytics.ts\`|

\*ExtractedTypeTable: Could not extract "UmamiAnalyticsOptions" from "./packages/scripts/src/vendors/analytics/umami-analytics.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*

### Script

|Property|Value|
|:--|:--|
|Type Name|\`Script\`|
|Source Path|\`./packages/core/src/libs/script-loader/types.ts\`|

\*ExtractedTypeTable: Could not extract "Script" from "./packages/core/src/libs/script-loader/types.ts" using base path "/home/runner/work/c15t/c15t". Verify the path/name and that the file is included by your tsconfig.\*
