npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

studios-sdk

v0.1.0

Published

Official Studios tracking SDK for JavaScript & React

Readme

Studios SDK

Official JavaScript & React SDK for Studios - prevent SaaS churn with intelligent tracking.

Installation

npm install studios-sdk
# or
yarn add studios-sdk
# or
pnpm add studios-sdk

Quick Start

JavaScript/TypeScript

import Studios from 'studios-sdk';

const studios = Studios.init({ appId: 'YOUR_APP_ID' });

// Identify users
await studios.identify('user_123', {
  email: '[email protected]',
  name: 'Sarah Chen',
  plan: 'pro',
  mrr: 4900, // in cents
});

// Track events
studios.track('user_123', 'feature_used', {
  feature: 'exports'
});

// Track support tickets
studios.supportTicket('user_123', {
  status: 'open',
  subject: 'How do I export data?'
});

React

import { StudiosProvider, useTrack } from 'studios-sdk/react';

function App() {
  return (
    <StudiosProvider config={{ appId: 'YOUR_APP_ID' }}>
      <Dashboard />
    </StudiosProvider>
  );
}

function Dashboard() {
  const track = useTrack();

  const handleExport = () => {
    track(user.id, 'export_clicked', { format: 'csv' });
  };

  return <button onClick={handleExport}>Export</button>;
}

Features

  • ✅ TypeScript support with full type definitions
  • ✅ Browser + Node.js (universal)
  • ✅ React hooks for easy integration
  • ✅ Auto-batching (10 events or 5 seconds)
  • ✅ Automatic retries with exponential backoff
  • ✅ Zero dependencies
  • ✅ <5kb gzipped

API Reference

Studios.init(config)

Initialize the Studios SDK.

Parameters:

  • appId (string, required) - Your Studios app ID
  • endpoint (string, optional) - API endpoint (defaults to production)
  • batchSize (number, optional) - Events per batch (default: 10)
  • batchInterval (number, optional) - Batch interval in ms (default: 5000)
  • retryAttempts (number, optional) - Retry attempts (default: 1)
  • debug (boolean, optional) - Enable debug logging (default: false)

Example:

const studios = Studios.init({
  appId: 'app_xxxxxxxx',
  debug: process.env.NODE_ENV === 'development'
});

studios.identify(userId, traits)

Identify a user and their attributes. Call this when a user signs up or their attributes change.

Parameters:

  • userId (string, required) - User identifier
  • traits.email (string, required) - User email
  • traits.name (string, optional) - User name
  • traits.company (string, optional) - Company name
  • traits.plan (string, optional) - Subscription plan
  • traits.mrr (number, optional) - Monthly recurring revenue in cents
  • traits.ltv (number, optional) - Lifetime value in cents
  • traits.signupDate (number, optional) - Signup date (unix timestamp in ms)
  • traits.properties (object, optional) - Custom properties

Example:

await studios.identify('user_123', {
  email: '[email protected]',
  name: 'Sarah Chen',
  company: 'Acme Corp',
  plan: 'pro',
  mrr: 9900, // $99.00 in cents
  signupDate: Date.now()
});

studios.track(userId, event, properties?)

Track a user event. Events are automatically batched and sent every 10 events or 5 seconds.

Parameters:

  • userId (string, required) - User identifier
  • event (string, required) - Event name (use snake_case)
  • properties (object, optional) - Event properties

Example:

studios.track('user_123', 'feature_used', {
  feature: 'exports',
  format: 'csv',
  rows: 1000
});

studios.track('user_123', 'project_created', {
  name: 'My Project',
  template: 'blank'
});

Recommended Events:

  • user_signed_up - New user registration
  • trial_started - Trial activation
  • subscription_started - Paid conversion
  • subscription_upgraded - Plan upgrade
  • subscription_canceled - Cancellation
  • feature_used - Core feature usage
  • export_* / import_* - Data operations
  • integration_connected - Third-party integrations
  • rate_limit_hit - Usage limits reached
  • payment_failed - Payment issues

studios.supportTicket(userId, options)

Track a support ticket to detect support silence patterns.

Parameters:

  • userId (string, required) - User identifier
  • options.status (string, required) - Ticket status: 'open' | 'pending' | 'resolved' | 'closed'
  • options.ticketId (string, optional) - External ticket ID
  • options.subject (string, optional) - Ticket subject

Example:

// When ticket is created
await studios.supportTicket('user_123', {
  status: 'open',
  ticketId: 'TICKET-456',
  subject: 'How do I export data?'
});

// When ticket is resolved
await studios.supportTicket('user_123', {
  status: 'resolved',
  ticketId: 'TICKET-456'
});

studios.flush()

Manually flush the event queue. Usually not needed as auto-batching handles this.

Example:

await studios.flush(); // Send all queued events immediately

React Hooks

<StudiosProvider>

Wrap your app with this provider to enable hooks.

Props:

  • config (StudiosConfig, required) - SDK configuration

Example:

import { StudiosProvider } from 'studios-sdk/react';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <StudiosProvider config={{
          appId: process.env.NEXT_PUBLIC_STUDIOS_APP_ID!,
          debug: process.env.NODE_ENV === 'development'
        }}>
          {children}
        </StudiosProvider>
      </body>
    </html>
  );
}

useStudios()

Access the Studios client in any component within StudiosProvider.

Returns: Studios instance

Example:

import { useStudios } from 'studios-sdk/react';

function SignupForm() {
  const studios = useStudios();

  const handleSignup = async (email, name) => {
    const userId = await createUser(email, name);

    await studios.identify(userId, {
      email,
      name,
      plan: 'trial',
      signupDate: Date.now()
    });
  };

  return <form onSubmit={handleSignup}>...</form>;
}

useTrack()

Convenience hook for tracking events with automatic memoization.

Returns: (userId: string, event: string, properties?: object) => void

Example:

import { useTrack } from 'studios-sdk/react';

function Dashboard({ user }) {
  const track = useTrack();

  const handleExport = async () => {
    track(user.id, 'export_started', { format: 'csv' });

    const data = await exportData();

    track(user.id, 'export_completed', {
      format: 'csv',
      rows: data.length
    });
  };

  return <button onClick={handleExport}>Export</button>;
}

Integration Guide

Next.js App Router

1. Add environment variable:

Create .env.local:

NEXT_PUBLIC_STUDIOS_APP_ID=app_xxxxxxxx

2. Wrap your app with StudiosProvider:

app/layout.tsx:

import { StudiosProvider } from 'studios-sdk/react';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <StudiosProvider config={{
          appId: process.env.NEXT_PUBLIC_STUDIOS_APP_ID!
        }}>
          {children}
        </StudiosProvider>
      </body>
    </html>
  );
}

3. Track events in components:

'use client';

import { useStudios, useTrack } from 'studios-sdk/react';

export default function Page() {
  const track = useTrack();

  return <button onClick={() => track(user.id, 'button_clicked')}>
    Click Me
  </button>;
}

Node.js Backend

import Studios from 'studios-sdk';

const studios = Studios.init({
  appId: process.env.STUDIOS_APP_ID!
});

// In your API routes
app.post('/api/signup', async (req, res) => {
  const { email, name } = req.body;
  const userId = await createUser(email, name);

  await studios.identify(userId, {
    email,
    name,
    plan: 'trial',
    signupDate: Date.now()
  });

  res.json({ success: true });
});

// Flush on shutdown
process.on('SIGTERM', async () => {
  await studios.flush();
  process.exit(0);
});

Browser (Vanilla JS)

<script type="module">
  import Studios from 'https://unpkg.com/studios-sdk/dist/index.mjs';

  const studios = Studios.init({ appId: 'YOUR_APP_ID' });

  studios.track('user_123', 'page_viewed', {
    path: window.location.pathname
  });
</script>

Configuration Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | appId | string | required | Your Studios app ID | | endpoint | string | https://grand-condor-228.convex.cloud | API endpoint | | batchSize | number | 10 | Events per batch | | batchInterval | number | 5000 | Batch interval in ms | | retryAttempts | number | 1 | Retry attempts on failure | | debug | boolean | false | Enable debug logging |

Batching Behavior

Events are automatically batched to reduce API calls:

  • Events are queued in memory
  • Batch is sent when 10 events are queued OR 5 seconds elapse
  • Use flush() to send immediately (e.g., on page unload)
// Batch will flush after 10 events or 5 seconds
studios.track('user_1', 'event_1');
studios.track('user_1', 'event_2');
// ... 8 more events triggers immediate flush

// Or flush manually
window.addEventListener('beforeunload', () => {
  studios.flush();
});

Error Handling

The SDK gracefully handles errors and won't crash your app:

// Errors are logged but don't throw
studios.track('user_1', 'event'); // Won't crash on network error

// For critical operations, use try/catch
try {
  await studios.identify(userId, { email });
} catch (error) {
  console.error('Failed to identify user:', error);
  // Handle error (e.g., retry logic, fallback)
}

TypeScript Support

Full TypeScript definitions are included:

import Studios, { StudiosConfig, IdentifyTraits } from 'studios-sdk';

const config: StudiosConfig = {
  appId: 'app_xxx',
  debug: true
};

const traits: IdentifyTraits = {
  email: '[email protected]',
  plan: 'pro',
  mrr: 9900
};

const studios = Studios.init(config);
await studios.identify('user_123', traits);

Examples

See the examples/ directory for complete examples:

  • browser.html - Vanilla JavaScript in browser
  • next-app.tsx - Next.js App Router integration
  • node.ts - Node.js backend integration

License

MIT

Support