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

@stevederico/skateboard-ui

v4.9.0

Published

React component library for rapid application development. Built with TypeScript, TailwindCSS v4, and shadcn-style primitives.

Downloads

3,618

Readme

skateboard-ui

React component library for rapid application development. Built with TypeScript, TailwindCSS v4, and shadcn-style primitives.

Requires Node.js 24+ in the app repo (see engines in package.json).

Installation

# npm
npm install @stevederico/skateboard-ui

# deno
deno install npm:@stevederico/skateboard-ui

Quick Start

import './assets/styles.css';
import { createSkateboardApp } from '@stevederico/skateboard-ui/App';
import constants from './constants.json';
import HomeView from './components/HomeView.jsx';

const appRoutes = [
  { path: 'home', element: <HomeView /> }
];

createSkateboardApp({ constants, appRoutes });

That's it — routing, auth, layout, landing page, settings, and payments are wired up.

Configuration

skateboard-ui requires a constants object:

const constants = {
  // Required
  devBackendURL: "http://localhost:8000/api",
  backendURL: "https://api.myapp.com/api",
  appName: "MyApp",
  appIcon: "sparkles",
  tagline: "Build apps faster with skateboard-ui",
  cta: "Get Started",
  features: {
    title: "Everything you need",
    items: [
      { icon: "Zap", title: "Fast", description: "Built for speed" },
      { icon: "Shield", title: "Secure", description: "Authentication included" }
    ]
  },
  companyName: "Your Company",
  companyWebsite: "https://yourcompany.com",
  companyEmail: "[email protected]",

  // Optional
  pages: [
    { title: "Home", icon: "home", url: "home" },
    { title: "Search", icon: "search", url: "search" }
  ],
  noLogin: false,
  hideSidebar: false,
  hideTabBar: false,
  hideSidebarHeader: false,
  stripeProducts: [
    {
      name: "Pro Plan",
      priceId: "price_123",
      price: "$10/month",
      lookup_key: "pro_plan",
      title: "Go Pro",
      interval: "month",
      features: ["Unlimited usage", "Priority support"]
    }
  ],
  navLinks: [
    { label: "Features", href: "#features" },
    { label: "Pricing", href: "#pricing" }
  ],
  pricing: { title: "Simple Pricing", extras: ["Priority support", "Cancel anytime"] },
  ctaHeading: "Ready To Build?",
  footerLinks: [
    { label: "Privacy", href: "/privacy" },
    { label: "Terms", href: "/terms" }
  ],
  copyrightText: "All rights reserved.",
  termsOfService: "Terms of Service for _COMPANY_...",
  privacyPolicy: "Privacy Policy for _COMPANY_...",
  EULA: "End User License Agreement...",
  subscriptionDetails: "Subscription details...",
  version: "1.0.0"
}

devBackendURL and backendURL should include the full API base path (e.g. /api). Endpoints are relative to that base:

  • ${getBackendURL()}/signuphttp://localhost:8000/api/signup
  • ${getBackendURL()}/mehttp://localhost:8000/api/me

createSkateboardApp

import { createSkateboardApp } from '@stevederico/skateboard-ui/App';

createSkateboardApp({
  constants,       // Required
  appRoutes,       // Required: [{ path: string, element: JSX.Element }]
  defaultRoute,    // Optional: defaults to first appRoute path
  landingPage,     // Optional: custom landing page element
  wrapper,         // Optional: component to wrap the router
});

Sets up routing, auth, theming, state, and error handling.

| Route | Component | Protected | |-------|-----------|-----------| | / | LandingView (or custom landingPage) | No | | /signin, /signup, /signout | SignIn/SignUp/SignOutView | No | | /app/:path | Your appRoutes | Yes | | /app/settings, /app/payment | SettingsView, PaymentView | Yes | | /terms, /privacy, /eula, /subscription | TextView | No | | * | NotFound | No |

Authentication

Hybrid cookie + localStorage auth:

  1. Sign in — backend sets {appName}_token (HttpOnly) and csrf_token cookies; frontend stores CSRF + user in localStorage
  2. Client checkisAuthenticated() reads localStorage (fast, no network)
  3. Route guardProtectedRoute validates via GET /me
  4. API calls — cookies sent automatically; X-CSRF-Token header on state-changing requests

Required backend endpoints

POST /signup and POST /signin

// Request
{ "email": "[email protected]", "password": "securePassword123", "name": "John Doe" }

// Response (201 / 200)
// Set-Cookie: {appName}_token=...; HttpOnly; Secure; SameSite=Strict
// Set-Cookie: csrf_token=...
{ "csrfToken": "...", "user": { "id": "...", "email": "...", "name": "..." } }

GET /me — validate session, return user (401 if not authenticated)

POST /signout — requires X-CSRF-Token, clears cookies

Optional endpoints

| Endpoint | Purpose | |----------|---------| | POST /usage | Track/check feature usage limits | | GET /isSubscriber | Subscription status | | POST /checkout | Stripe checkout session | | POST /portal | Stripe billing portal |

Lazy auth (default)

Users can browse /app without signing in. Protected actions trigger the auth overlay via useAuthGate:

import { useAuthGate } from '@stevederico/skateboard-ui/useAuthGate';

function SaveButton() {
  const requireAuth = useAuthGate();
  return <button onClick={() => requireAuth(() => saveThing())}>Save</button>;
}

Set authOverlay: false in constants to require sign-in before /app access.

No-login mode

const constants = { noLogin: true };

Troubleshooting

| Problem | Fix | |---------|-----| | "Not authenticated" after signin | Ensure credentials: 'include' in fetch calls | | CSRF 403 errors | Verify X-CSRF-Token header and csrf_token cookie | | Cookies not persisting (dev) | Set secure: false in dev, check domain match |

Dark Mode

Add this script to index.html before your app loads to prevent FOUC:

<script>
  try {
    const theme = localStorage.getItem('theme');
    const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    if (theme === 'dark' || (!theme && systemDark)) {
      document.documentElement.classList.add('dark');
    }
  } catch (e) {}
</script>

Styling

@import "@stevederico/skateboard-ui/styles.css";

@source '../../node_modules/@stevederico/skateboard-ui';

@theme {
  --color-app: var(--color-purple-500);
}

Key variables: --color-app (brand), --background, --foreground, --accent, --radius.

Components

App components

| Component | Import | Description | |-----------|--------|-------------| | Sidebar | @stevederico/skateboard-ui/Sidebar | Desktop navigation sidebar | | Header | @stevederico/skateboard-ui/Header | Page header with optional action button | | Layout | @stevederico/skateboard-ui/Layout | Sidebar (desktop) + tabbar (mobile) | | TabBar | @stevederico/skateboard-ui/TabBar | Mobile bottom navigation | | DynamicIcon | @stevederico/skateboard-ui/DynamicIcon | Lucide icon by name string | | ThemeToggle | @stevederico/skateboard-ui/ThemeToggle | Dark/light mode toggle | | Sheet | @stevederico/skateboard-ui/Sheet | Slide-out panel | | UpgradeSheet | @stevederico/skateboard-ui/UpgradeSheet | Premium upgrade drawer | | ErrorBoundary | @stevederico/skateboard-ui/ErrorBoundary | Error boundary wrapper |

Views

| Component | Import | |-----------|--------| | LandingView | @stevederico/skateboard-ui/LandingView | | SignInView | @stevederico/skateboard-ui/SignInView | | SignUpView | @stevederico/skateboard-ui/SignUpView | | SignOutView | @stevederico/skateboard-ui/SignOutView | | SettingsView | @stevederico/skateboard-ui/SettingsView | | PaymentView | @stevederico/skateboard-ui/PaymentView | | TextView | @stevederico/skateboard-ui/TextView | | NotFound | @stevederico/skateboard-ui/NotFound |

Auth

| Export | Import | |--------|--------| | AuthOverlay | @stevederico/skateboard-ui/AuthOverlay | | useAuthGate | @stevederico/skateboard-ui/useAuthGate | | ProtectedRoute | @stevederico/skateboard-ui/ProtectedRoute |

UI primitives

Import from @stevederico/skateboard-ui/ui/*.

import { Button } from '@stevederico/skateboard-ui/ui/button';
import { Card, CardHeader, CardTitle, CardContent } from '@stevederico/skateboard-ui/ui/card';
import { Input } from '@stevederico/skateboard-ui/ui/input';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@stevederico/skateboard-ui/ui/dialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@stevederico/skateboard-ui/ui/select';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@stevederico/skateboard-ui/ui/dropdown-menu';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@stevederico/skateboard-ui/ui/tabs';
import { cn } from '@stevederico/skateboard-ui/shadcn/lib/utils';
import { useIsMobile } from '@stevederico/skateboard-ui/shadcn/hooks/use-mobile';

Icons

import { ArrowUp, X } from '@stevederico/skateboard-ui/icons';

Context

import { getState, useUser, useDispatch } from '@stevederico/skateboard-ui/Context';

const { state, dispatch } = getState();
dispatch({ type: 'SET_USER', payload: userData });
dispatch({ type: 'CLEAR_USER' });

| Hook | Re-renders on | |------|---------------| | getState() | Any state change | | useUser() | User changes only | | useDispatch() | Never (stable) |

| Action | Payload | Description | |--------|---------|-------------| | SET_USER | user object | Set authenticated user | | CLEAR_USER | — | Clear user | | SET_SIDEBAR_VISIBLE | boolean | Show/hide sidebar | | SET_TABBAR_VISIBLE | boolean | Show/hide tab bar | | SHOW_AUTH_OVERLAY | callback or null | Show auth dialog | | HIDE_AUTH_OVERLAY | — | Hide auth dialog | | AUTH_OVERLAY_SUCCESS | — | Run pending callback and close |

Utilities

import {
  apiRequest,
  apiRequestWithParams,
  isAuthenticated,
  getCurrentUser,
  isSubscriber,
  getCSRFToken,
  getBackendURL,
  getConstants,
  getRemainingUsage,
  trackUsage,
  showCheckout,
  showManage,
  showUpgradeSheet,
  showSidebar,
  hideSidebar,
  showTabBar,
  hideTabBar,
  timestampToString,
  useListData,
  useForm,
  isAppMode,
} from '@stevederico/skateboard-ui/Utilities';
// API — auto-includes credentials, CSRF header, auth overlay on 401
const data = await apiRequest('/deals');
await apiRequest('/deals', { method: 'POST', body: JSON.stringify({ name: 'New Deal' }) });

// Data fetching
const { data, loading, error, refetch } = useListData('/deals');

// Timestamps
timestampToString(1706000000, 'ago');   // "2 hours ago"
timestampToString(1706000000, 'DOB');   // "Jan 23, 2024"

// Stripe
showCheckout('[email protected]', 0);
showManage('cus_abc123');

Peer dependencies

  • React 19.1+
  • react-dom 19.1+
  • react-router 7.0+

Repository

https://github.com/stevederico/skateboard-ui

Version history and migration notes: CHANGELOG.md