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

@loomio/sdk

v0.1.2

Published

React SDK for integrating Loomio CMS with client websites

Readme

@loomio/sdk

Framework-agnostic SDK for integrating Loomio CMS with your client websites. This package provides multiple entry points for different use cases: core client, React hooks with server-side rendering support, and server-only utilities.

Installation

npm install @loomio/sdk
# or
pnpm add @loomio/sdk
# or
yarn add @loomio/sdk

Package Structure

This SDK is modular with separate entry points:

  • @loomio/sdk - Core client and global config management
  • @loomio/sdk/react - React hooks with TanStack Query
  • @loomio/sdk/server - Server-side utilities with caching support
  • @loomio/sdk/client - Framework-agnostic client class

Quick Start

Global Configuration (Recommended)

Define your configuration once at the root of your application:

// lib/loomio-config.ts
import { defineConfig } from '@loomio/sdk';

export const loomioConfig = defineConfig({
  siteId: "your-site-id",
  apiUrl: "https://your-api.example.com", // Optional
});

⚠️ Server-Side Considerations:

  • defineConfig() uses a module-level global variable
  • Call it ONCE during app initialization, NOT per-request
  • In serverless/edge environments, ensure it's set during cold start
  • For per-request config changes, use explicit config parameters instead

React with Global Config

// app/layout.tsx
import './lib/loomio-config'; // Import to initialize config
import { LoomioProvider } from '@loomio/sdk/react';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <LoomioProvider>
          {children}
        </LoomioProvider>
      </body>
    </html>
  );
}
// components/HomePage.tsx
import { useSite, useGallery, useCustomValue } from '@loomio/sdk/react';

export function HomePage() {
  const { data: site, isLoading } = useSite();
  const { data: gallery } = useGallery();
  const { data: tagline } = useCustomValue('site_tagline');

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>{site?.name}</h1>
      <p>{tagline?.value}</p>

      <div className="gallery">
        {gallery?.map(image => (
          <img
            key={image._id}
            src={image.file?.url}
            alt={image.name}
          />
        ))}
      </div>
    </div>
  );
}

Server-Side Rendering (Next.js, etc.)

// app/page.tsx
import { getSite, getGallery } from '@loomio/sdk/server';
import './lib/loomio-config'; // Ensure config is loaded

export default async function Page() {
  // Uses global config, with optional overrides
  const site = await getSite({ revalidate: 3600 }); // Revalidate every hour
  const gallery = await getGallery({ tags: ['featured'] }, { revalidate: 300 });

  return (
    <div>
      <h1>{site.name}</h1>
      <div className="gallery">
        {gallery.map(image => (
          <img key={image._id} src={image.file?.url} alt={image.name} />
        ))}
      </div>
    </div>
  );
}

Theme Management

The SDK provides two-layer theme injection to eliminate theme flashing while maintaining reactivity:

1. Server-Side Theme Injection (Recommended)

Use the ThemeScript component to inject theme variables during server-side rendering, preventing the default theme from flashing:

// app/layout.tsx
import { ThemeScript } from '@loomio/sdk/server';
import { LoomioProvider, LoomioTheme } from '@loomio/sdk/react';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {/* Inject theme on server (no flash) */}
        <ThemeScript />
        <LoomioProvider>
          {/* Client component syncs theme changes */}
          <LoomioTheme />
          {children}
        </LoomioProvider>
      </body>
    </html>
  );
}

Benefits:

  • ✅ No theme flash on initial load
  • ✅ Correct theme in initial HTML (SEO-friendly)
  • ✅ Works with SSR/SSG
  • ✅ Better performance (no client fetch needed)
  • ✅ Zero configuration (uses global config automatically)

Advanced: Manual theme injection using generateThemeStyles():

import { getSite, generateThemeStyles } from '@loomio/sdk/server';

export default async function RootLayout({ children }) {
  const site = await getSite({ revalidate: 3600 });
  const themeCSS = generateThemeStyles(site.theme);

  return (
    <html>
      <head>
        <style dangerouslySetInnerHTML={{ __html: themeCSS }} />
      </head>
      <body>{children}</body>
    </html>
  );
}

2. Client-Side Theme Component

The LoomioTheme component handles dynamic theme updates:

<LoomioProvider>
  <LoomioTheme />
  {children}
</LoomioProvider>

The component:

  • Syncs theme changes from the dashboard in real-time
  • Handles system preference changes (light/dark mode toggle)
  • Updates theme variables without page reload
  • Works together with server-side injection

Theme Configuration Options

<LoomioTheme
  // Respect system dark mode preference (default: true)
  respectSystemPreference={true}

  // Force a specific theme mode
  forceMode="dark"

  // Custom dark mode class name (default: "dark")
  darkModeClass="dark-theme"
/>

Using Theme Variables

Once injected, you can use the theme variables in your CSS:

.my-component {
  background-color: hsl(var(--background));
  color: hsl(var(--foreground));
  border: 1px solid hsl(var(--border));
}

.button-primary {
  background-color: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
}

Available CSS variables include:

  • --background, --foreground
  • --primary, --primary-foreground
  • --secondary, --secondary-foreground
  • --muted, --muted-foreground
  • --accent, --accent-foreground
  • --destructive, --destructive-foreground
  • --border, --input, --ring
  • --card, --card-foreground
  • --popover, --popover-foreground
  • --sidebar-* (various sidebar colors)
  • --chart-1 through --chart-5
  • --radius (border radius)

Available Hooks

useSite()

Fetches site metadata including name, description, pages, contact info, and social media.

const { data: site, isLoading, error } = useSite();

useGallery(options?)

Fetches gallery images with optional tag filtering.

const { data: images } = useGallery({
  tags: ['featured'],
  enabled: true
});

useCustomValue(key)

Fetches a single custom value by key.

const { data: value } = useCustomValue('hero_title');

useCustomValues()

Fetches all custom values for the site.

const { data: values } = useCustomValues();

Configuration

Configuration Priority

The SDK uses the following priority order for configuration:

  1. Global config (from defineConfig()) merged with explicit overrides
  2. Explicit config parameter (if it contains siteId)
  3. Environment variables (you can use any environment variable name you prefer)

Configuration Options

  • siteId (required): The ID of the site to fetch data for
  • apiUrl (optional): Your Loomio API base URL. Falls back to environment or defaults

Alternative: Explicit Config (Without Global Config)

You can also use explicit configuration without global config:

// For React
<LoomioProvider
  config={{
    siteId: 'your-site-id',
    apiUrl: 'https://your-api.example.com',
  }}
>
  <YourApp />
</LoomioProvider>
// For server-side
const site = await getSite({
  siteId: 'your-site-id',
  apiUrl: 'https://your-api.example.com',
  revalidate: 3600,
});

Environment Variables

You can use any environment variable names you prefer. The SDK doesn't enforce specific environment variable names - you configure them when calling defineConfig(). Common examples include:

  • NEXT_PUBLIC_LOOMIO_SITE_ID, SITE_ID, LOOMIO_SITE_ID, etc. - Your site ID
  • NEXT_PUBLIC_LOOMIO_API_URL, API_URL, LOOMIO_API_URL, etc. - Your API URL

Simply pass the environment variable values to defineConfig() using whatever variable names fit your project's conventions.

Multiple Sites

If you need to work with multiple sites in the same application, don't use global config. Instead, pass explicit config to each provider/function call:

// Site 1
<LoomioProvider config={{ siteId: 'site-1' }}>
  <Site1Content />
</LoomioProvider>

// Site 2
<LoomioProvider config={{ siteId: 'site-2' }}>
  <Site2Content />
</LoomioProvider>

Features

  • 🌐 Framework-Agnostic: Core client works with any JavaScript framework
  • 🔄 Reactive Data: React hooks built on TanStack Query for automatic caching and refetching
  • 🎯 Type-Safe: Full TypeScript support with generated types
  • Optimized: Smart caching strategies to minimize API calls
  • 🚀 SSR Support: Server-side rendering with configurable caching (Next.js, Remix, etc.)
  • 📦 Tree-Shakeable: Modular exports for optimal bundle size
  • 🎨 Flexible: Global config or explicit config - you choose
  • 🔌 Extensible: Easy to add custom hooks for your specific needs

Future Enhancements

This SDK is designed to grow with your needs. Future versions will include:

  • Real-time subscriptions via Convex
  • Product catalog hooks
  • Search functionality
  • Pagination support
  • Optimistic updates
  • And more!

Development

# Install dependencies
pnpm install

# Build the package
pnpm build

# Watch mode for development
pnpm dev

# Type checking
pnpm typecheck

License

MIT