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

@wocha/ui

v0.1.0

Published

Themed, customisable UI components for Wocha authentication

Readme

@wocha/ui

npm version npm downloads TypeScript License

Themed, customisable React components for Wocha authentication. Works with both @wocha/react (SPAs) and @wocha/nextjs (App Router).

Install

npm install @wocha/ui @wocha/react
# or
npm install @wocha/ui @wocha/nextjs

Import the stylesheet once in your app:

import "@wocha/ui/styles.css";

Quick start

React SPA

import { WochaProvider } from "@wocha/react";
import { WochaThemeProvider, SignIn, UserButton } from "@wocha/ui";
import "@wocha/ui/styles.css";

export function App() {
  return (
    <WochaProvider config={greetConfig}>
      <WochaThemeProvider>
        <header>
          <UserButton profileUrl="/profile" organisationUrl="/settings/org" />
        </header>
        <main>
          <SignIn redirectUrl="/dashboard" />
        </main>
      </WochaThemeProvider>
    </WochaProvider>
  );
}

Next.js App Router

// app/layout.tsx
import { WochaSessionProvider } from "@wocha/nextjs/client";
import { WochaThemeProvider } from "@wocha/ui";
import "@wocha/ui/styles.css";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <WochaSessionProvider apiUrl={process.env.WOCHA_API_URL}>
          <WochaThemeProvider sdk="nextjs" apiUrl={process.env.WOCHA_API_URL}>
            {children}
          </WochaThemeProvider>
        </WochaSessionProvider>
      </body>
    </html>
  );
}

Components

<SignIn>

A styled sign-in form that redirects to Wocha hosted login.

import { SignIn } from "@wocha/ui";

<SignIn
  redirectUrl="/dashboard"
  organisation="org_abc123"
  providers={["google", "github"]}
  appearance={{
    variables: { "--wocha-color-primary": "#6366f1" },
  }}
/>

Props

| Prop | Type | Description | |------|------|-------------| | redirectUrl | string | URL to return to after sign-in | | organisation | string | Organisation ID hint for hosted login | | providers | SocialProvider[] | Social buttons to show (default: all four) | | appearance | WochaAppearance | Per-component theme overrides | | withCard | boolean | Wrap in AuthCard (default: true) | | title | ReactNode | Card heading | | description | ReactNode | Card subheading | | buttonLabel | string | Primary button text |

Individual import:

import { SignIn } from "@wocha/ui/sign-in";

<SignUp>

Registration flow — same API as <SignIn> but redirects to hosted registration.

import { SignUp } from "@wocha/ui";

<SignUp
  redirectUrl="/welcome"
  providers={["google", "apple"]}
  title="Join Acme Corp"
  description="Create an account to get started."
/>

<UserButton>

Dropdown showing the current user's avatar, name, and email.

import { UserButton } from "@wocha/ui";

// Full variant (default) — avatar, name, email, chevron
<UserButton
  variant="full"
  profileUrl="/profile"
  organisationUrl="/settings/organisation"
/>

// Compact — avatar only
<UserButton variant="compact" />

Menu items: Profile, Organisation, Sign out. Supports keyboard navigation (Arrow keys, Enter, Escape) and ARIA menu semantics.

<OrgSwitcher>

Organisation switcher dropdown for multi-org users.

import { OrgSwitcher } from "@wocha/ui";

<OrgSwitcher
  getOrgLabel={(id) => orgNames[id] ?? id}
  createOrganisationUrl="/organisations/new"
  showSingle
/>

Shows a checkmark on the active organisation. Includes a Create organisation action when createOrganisationUrl or onCreateOrganisation is provided.

<UserProfile>

Full-page profile management with sections for profile details, security, sessions, and organisations. Uses the Management API with the user's OAuth access token — requires apiUrl on your provider.

import { UserProfile } from "@wocha/ui";

<UserProfile
  sections={["profile", "security", "sessions", "connections", "organisations"]}
  onSave={(data) => console.log("Saved", data)}
  appearance={{ theme: "light" }}
/>

Sections

| Section | Features | |---------|----------| | profile | Display name, avatar placeholder, read-only email, metadata display | | security | Password change form, passkey/MFA placeholders | | sessions | Active sessions with device, IP, last active, revoke button | | connections | Linked social accounts with connect/disconnect actions | | organisations | Orgs the user belongs to with role badges |

Props

| Prop | Type | Description | |------|------|-------------| | sections | ProfileSection[] | Sections to render (default: all four) | | activeSection | ProfileSection | Controlled active section | | onSectionChange | (section) => void | Called when section changes | | onSave | (data) => void | Called after profile save succeeds | | appearance | WochaAppearance | Per-component theme overrides | | hideNavigation | boolean | Hide internal tabs (use with UserPortal) |

Individual import:

import { UserProfile, useProfile } from "@wocha/ui/user-profile";

<UserPortal>

Standalone full-page self-service portal with header, sidebar navigation, and sign-out button. Mount at a dedicated route (e.g. /account).

// app/account/page.tsx (Next.js)
import { UserPortal } from "@wocha/ui";

export default function AccountPage() {
  return (
    <UserPortal
      title="My account"
      sections={["profile", "security", "sessions", "connections", "organisations"]}
    />
  );
}

Link from <UserButton profileUrl="/account" /> to connect the dropdown to the portal.

Props

| Prop | Type | Description | |------|------|-------------| | title | string | Portal heading (default: "Account settings") | | logo | ReactNode | Custom logo in header | | sections | ProfileSection[] | Sidebar sections to show | | appearance | WochaAppearance | Theme overrides |

useProfile()

Hook for programmatic profile management. Returns user data, mutations, and loading state.

import { useProfile } from "@wocha/ui";

const {
  user,
  updateProfile,
  changePassword,
  sessions,
  revokeSession,
  organisations,
  isLoading,
  error,
  refresh,
} = useProfile();

await updateProfile({ name: "Alice Smith" });
await changePassword("old-pass", "new-pass");
await revokeSession("ses_abc123");

Requires apiUrl on WochaProvider (React) or WochaSessionProvider (Next.js).

<Protect>

Conditional rendering based on authentication, permissions, or roles.

import { Protect } from "@wocha/ui";

// Auth-only gate
<Protect fallback={<p>Please sign in.</p>}>
  <Dashboard />
</Protect>

// Role gate
<Protect role="admin" fallback={<p>Admins only.</p>}>
  <AdminPanel />
</Protect>

// Permission gate (SpiceDB)
<Protect
  permission={{
    resource: { type: "document", id: "doc_123" },
    permission: "edit",
  }}
  fallback={<p>You cannot edit this document.</p>}
>
  <EditForm />
</Protect>

Combine role and permission — both must pass.

<AuthCard>

Styled card wrapper for auth UI. Used internally by <SignIn> and <SignUp> but available standalone.

import { AuthCard, WochaLogo } from "@wocha/ui";

<AuthCard
  logo={<WochaLogo className="wocha-card__logo-icon" />}
  title="Reset password"
  description="Enter your email to receive a reset link."
  footer={<a href="/sign-in">Back to sign in</a>}
>
  {/* custom form content */}
</AuthCard>

Theming

All styles use CSS custom properties scoped with the .wocha- prefix to avoid conflicts with your app.

Default theme

Dark background (#1c1917), gold accent (#D4AF37), clean system typography.

Light mode

Set data-theme="light" on the theme root, or use the provider:

<WochaThemeProvider appearance={{ theme: "light" }}>
  {children}
</WochaThemeProvider>

System preference:

<WochaThemeProvider appearance={{ theme: "system" }}>
  {children}
</WochaThemeProvider>

Custom variables

Override any CSS variable globally or per component:

<WochaThemeProvider
  appearance={{
    theme: "dark",
    radius: "lg",
    fontSize: "md",
    variables: {
      "--wocha-color-primary": "#818cf8",
      "--wocha-color-background": "#0f0f0f",
      "--wocha-font-family": "'Inter', sans-serif",
    },
  }}
>
  {children}
</WochaThemeProvider>

Available variables:

| Variable | Purpose | |----------|---------| | --wocha-color-background | Page background | | --wocha-color-foreground | Primary text | | --wocha-color-card | Card / dropdown background | | --wocha-color-primary | Buttons, avatar, accents | | --wocha-color-muted-foreground | Secondary text | | --wocha-color-border | Borders | | --wocha-color-destructive | Sign out, errors | | --wocha-radius | Border radius scale | | --wocha-font-size-base | Base font size | | --wocha-spacing-* | xs, sm, md, lg, xl | | --wocha-shadow-* | sm, md, lg |

Raw CSS

Import and extend the stylesheet directly:

@import "@wocha/ui/styles.css";

.wocha-root[data-theme="light"] {
  --wocha-color-primary: #7c3aed;
}

Layout example

A complete unauthenticated / authenticated layout:

import {
  WochaThemeProvider,
  SignIn,
  SignUp,
  UserButton,
  OrgSwitcher,
  Protect,
  useWochaUiAuth,
} from "@wocha/ui";

function Header() {
  return (
    <header style={{ display: "flex", gap: "1rem", alignItems: "center" }}>
      <OrgSwitcher getOrgLabel={(id) => id.replace("org_", "")} />
      <UserButton variant="full" profileUrl="/profile" />
    </header>
  );
}

function HomePage() {
  const { isAuthenticated, isLoading } = useWochaUiAuth();

  if (isLoading) return <p>Loading…</p>;

  if (!isAuthenticated) {
    return (
      <div style={{ display: "grid", gap: "2rem", maxWidth: "24rem", margin: "4rem auto" }}>
        <SignIn redirectUrl="/dashboard" />
        <SignUp redirectUrl="/welcome" />
      </div>
    );
  }

  return (
    <>
      <Header />
      <Protect
        permission={{ resource: { type: "app", id: "main" }, permission: "view" }}
        fallback={<p>Access denied.</p>}
      >
        <main>Protected content</main>
      </Protect>
    </>
  );
}

export function AppShell() {
  return (
    <WochaThemeProvider appearance={{ theme: "dark" }}>
      <HomePage />
    </WochaThemeProvider>
  );
}

Note: useWochaUiAuth exposes the unified auth bridge used internally by UI components. For general app logic, prefer useSession / useWochaAuth from your SDK.

Individual imports

Tree-shake by importing components directly:

import { SignIn } from "@wocha/ui/sign-in";
import { UserButton } from "@wocha/ui/user-button";
import { Protect } from "@wocha/ui/protect";
import { AuthCard } from "@wocha/ui/auth-card";
import { OrgSwitcher } from "@wocha/ui/org-switcher";
import { SignUp } from "@wocha/ui/sign-up";
import { UserProfile, UserPortal, useProfile } from "@wocha/ui/user-profile";

Accessibility

  • All interactive elements have visible focus rings (:focus-visible)
  • Dropdowns use role="menu" / role="listbox" with keyboard navigation
  • Social buttons include aria-label with provider name
  • Avatar initials served as fallback when no profile image is available
  • Separator elements use role="separator"

Requirements

  • React 18+
  • One of @wocha/react or @wocha/nextjs (peer dependency)
  • No CSS framework required — pure CSS custom properties

License

Apache-2.0

Enterprise widgets

Embeddable React components for enterprise self-service — SSO, SCIM, and domain verification. Each widget works standalone with a portal token generated from the Wocha Console.

npm install @wocha/ui
import "@wocha/ui/styles.css";
import { EnterpriseSetup, SSOSetup, SCIMSetup, DomainVerification } from "@wocha/ui/enterprise";

export function CustomerOnboarding({ portalToken }: { portalToken: string }) {
  return (
    <EnterpriseSetup
      portalToken={portalToken}
      portalBaseUrl="https://console.wocha.ai"
      appearance={{ theme: "light" }}
      onComplete={() => console.log("Enterprise setup complete")}
    />
  );
}

<EnterpriseSetup>

Combined tabbed widget with SSO, SCIM, and domain verification plus an overall progress indicator.

| Prop | Type | Description | |------|------|-------------| | portalToken | string | Portal token from the Wocha Console | | portalBaseUrl | string | Console origin hosting portal APIs (default: https://console.wocha.ai) | | onComplete | () => void | Called when a section completes successfully | | appearance | WochaAppearance | Theme overrides (same CSS variables as auth components) | | className | string | Additional root class name |

<SSOSetup>

Multi-step SAML/OIDC wizard with domain verification and connection testing.

<SSOSetup
  portalToken={token}
  onComplete={() => router.push("/done")}
  appearance={{
    variables: { "--wocha-color-primary": "#2563eb" },
  }}
/>

<SCIMSetup>

Shows SCIM endpoint and bearer token, IdP-specific setup guides, and connection testing.

<SCIMSetup portalToken={token} onComplete={handleDone} />

<DomainVerification>

Add domains, display DNS TXT records, and verify ownership.

<DomainVerification
  portalToken={token}
  onVerified={(domain) => console.log(`${domain} verified`)}
/>

Standalone usage

Each widget wraps its own portal provider and calls the Console portal API internally:

  • GET /api/portal/validate?token=…
  • POST /api/portal/setup with Authorization: Bearer {portalToken}

Point portalBaseUrl at your Wocha Console deployment when self-hosting.

Theming

Enterprise widgets use the same .wocha- CSS custom properties as auth components. Wrap in WochaThemeProvider or pass appearance directly to each widget.

Accessibility

  • Step indicators and tab navigation use semantic lists and aria-selected
  • Loading states expose role="status"
  • Errors use role="alert"
  • All controls are keyboard accessible with visible focus rings