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

tyrell-react

v1.0.0-RC9

Published

React wrappers for Tyrell Components

Readme

tyrell-react

React wrappers for Tyrell Web Components - bringing framework-agnostic web components to React with full TypeScript support.

🎯 Philosophy

Tyrell web components are distributed via CDN and loaded as standard web components. These React wrappers provide:

  • React-friendly API - Props instead of attributes
  • TypeScript types - Full type safety
  • Event handling - React synthetic events
  • Ref forwarding - Direct DOM access
  • Zero bundle overhead - Just thin wrappers (~2KB)

📦 Installation

Step 1: Load Tyrell Web Components (CDN)

Add to your index.html:

<!DOCTYPE html>
<html>
<head>
  <!-- Tyrell Web Components & Styles via CDN -->
  <script src="https://cdn.jsdelivr.net/npm/tyrell-components@latest/dist/tyrell.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tyrell-components@latest/css/tyrell.css">
</head>
<body>
  <div id="root"></div>
</body>
</html>

Step 2: Install React Wrappers

npm install tyrell-react

That's it! No build complexity, no bundler configuration.

🚀 Quick Start

import React, { useState } from 'react';
import { TyButton, TyInput, TyModal } from 'tyrell-react';

function App() {
  const [value, setValue] = useState('');

  return (
    <div>
      <TyButton flavor="primary" onClick={() => alert('Hello!')}>
        Click Me
      </TyButton>
      
      <TyInput 
        placeholder="Enter text..."
        value={value}
        onChange={(e) => setValue(e.detail.value)}  // Fires on every keystroke
      />
    </div>
  );
}

📚 Available Components

All 18 components wrapped:

| Component | Description | |-----------|-------------| | TyButton | Semantic button with multiple flavors | | TyInput | Form input with validation | | TyTextarea | Multi-line text input | | TyCheckbox | Checkbox with custom styling | | TyDropdown | Dropdown selection | | TyOption | Dropdown option | | TyMultiselect | Multi-value selection | | TyTag | Tag/badge component | | TyModal | Modal dialogs | | TyPopup | Smart popup positioning | | TyTooltip | Tooltip component | | TyIcon | Icon component (3000+ icons) | | TyCalendar | Full calendar | | TyCalendarMonth | Month view | | TyCalendarNavigation | Calendar navigation | | TyDatePicker | Date picker with calendar | | TyTabs | Tab container | | TyTab | Individual tab | | TyWizard | Multi-step wizard/stepper | | TyStep | Individual wizard step | | TyResizeObserver | Element size observer | | TyScrollContainer | Scroll wrapper with edge shadows | | TyCopy | Copy-to-clipboard field |

⚡ Event Handling (React Convention)

Tyrell React wrappers follow React conventions for event handling:

Input Components (Input, Textarea, Checkbox)

import { TyInput } from 'tyrell-react';

function SearchBox() {
  const [query, setQuery] = useState('');
  
  return (
    <TyInput
      value={query}
      // ✅ onChange fires on every keystroke (React convention)
      onChange={(e) => setQuery(e.detail.value)}
      
      // ✅ onChangeCommit fires on blur (optional - for validation)
      onChangeCommit={(e) => console.log('Final value:', e.detail.value)}
    />
  );
}

Key Points:

  • onChange → Fires on every keystroke (matches React's <input onChange>)
  • onChangeCommit → Fires on blur if value changed (optional)
  • This differs from native DOM where onchange fires on blur

Selection Components (Dropdown, Calendar, etc.)

import { TyDropdown, TyOption } from 'tyrell-react';

function CountrySelector() {
  return (
    <TyDropdown 
      // onChange fires when selection changes
      onChange={(e) => console.log('Selected:', e.detail.value)}
    >
      <TyOption value="us">United States</TyOption>
      <TyOption value="ca">Canada</TyOption>
    </TyDropdown>
  );
}

For selection components, onChange fires when the selection changes (as expected).

💡 Usage Examples

Form with Validation

import { TyInput, TyDropdown, TyOption, TyButton } from 'tyrell-react';

function ContactForm() {
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget as HTMLFormElement);
    console.log(Object.fromEntries(formData));
  };

  return (
    <form onSubmit={handleSubmit}>
      <TyInput 
        name="email"
        type="email" 
        required 
        placeholder="[email protected]"
      />
      
      <TyDropdown name="role" required>
        <TyOption value="admin">Administrator</TyOption>
        <TyOption value="user">User</TyOption>
      </TyDropdown>
      
      <TyButton type="submit" flavor="primary">
        Submit
      </TyButton>
    </form>
  );
}

Modal with Imperative API

import { useRef } from 'react';
import { TyModal, TyButton, type TyModalRef } from 'tyrell-react';

function ModalExample() {
  const modalRef = useRef<TyModalRef>(null);

  return (
    <>
      <TyButton onClick={() => modalRef.current?.show()}>
        Open Modal
      </TyButton>
      
      <TyModal 
        ref={modalRef}
        onClose={(e) => console.log('Closed:', e.detail.reason)}
      >
        <h2>Modal Content</h2>
        <TyButton onClick={() => modalRef.current?.hide()}>
          Close
        </TyButton>
      </TyModal>
    </>
  );
}

Calendar with Custom Rendering

import { useState } from 'react';
import { TyCalendar } from 'tyrell-react';

function CalendarExample() {
  const [selected, setSelected] = useState<Date | null>(null);

  return (
    <TyCalendar
      value={selected?.getTime()}
      onChange={(e) => setSelected(new Date(e.detail.date))}
      min="2024-01-01"
      max="2024-12-31"
    />
  );
}

🪟 Surfaces

The Tyrell design system has five surface levels (background + shadow). They ship as CSS utility classes — apply them directly to any element. There is no wrapper component to learn:

function Dashboard() {
  return (
    <section className="ty-elevated p-6 rounded-lg">
      <h2>Card title</h2>
      <p>Sits above the page background.</p>
    </section>
  );
}

| Class | Use for | |----------------|--------------------------------------| | ty-floating | modals, dropdowns, tooltips | | ty-elevated | cards, panels, sidebars | | ty-content | main content areas | | ty-canvas | app/page background | | ty-input | form control surface |

Golden rule: use Tyrell surface/colour classes for colour, Tailwind (or your own utilities) for everything else (spacing, layout, typography). Don't mix bg-blue-500 with surfaces.

🎨 Icons (3000+ Available)

Import only the icons you need (tree-shaking) and register them once at app startup via the global window.tyIcons API exposed by tyrell-components:

// app/icons.ts (or wherever your app boots)
import { check, heart, star } from 'tyrell-components/icons/lucide';

export function registerIcons() {
  window.tyIcons.register({ check, heart, star });
}
// app/layout.tsx (call once, e.g. in a top-level effect)
'use client';
import { useEffect } from 'react';
import { registerIcons } from './icons';

export default function RootLayout({ children }) {
  useEffect(() => { registerIcons(); }, []);
  return <>{children}</>;
}

Then use icons anywhere in React:

import { TyIcon, TyButton } from 'tyrell-react';

function IconExample() {
  return (
    <>
      <TyIcon name="check" size="lg" />

      <TyButton flavor="primary">
        <TyIcon name="heart" />
        Like
      </TyButton>
    </>
  );
}

CDN alternative (vanilla HTML, no bundler):

<script type="module">
  import { check, heart, star }
    from 'https://cdn.jsdelivr.net/npm/tyrell-components@latest/lib/icons/lucide.js';

  window.tyIcons.register({ check, heart, star });
</script>

📘 TypeScript Support

Full TypeScript definitions included:

import type { 
  TyButtonProps,
  TyInputEventDetail,
  TyModalRef,
  TyDropdownEventDetail 
} from 'tyrell-react';

// Type-safe props
const buttonProps: TyButtonProps = {
  flavor: 'primary',  // ✅ Autocomplete
  size: 'lg',         // ✅ Type-checked
  disabled: false
};

// Type-safe event handlers
const handleChange = (e: CustomEvent<TyInputEventDetail>) => {
  console.log(e.detail.value);      // ✅ Typed
  console.log(e.detail.rawValue);   // ✅ Typed
};

🌐 ClojureScript Support

Works perfectly with Reagent and UIx:

Reagent

(ns my-app.core
  (:require ["tyrell-react" :as ty]))

(defn my-component []
  [:div
   [:> ty/TyButton {:flavor "primary"} "Click Me"]
   [:> ty/TyInput {:placeholder "Enter text..."}]])

UIx

(ns my-app.core
  (:require [uix.core :as uix]
            ["tyrell-react" :refer [TyButton TyInput]]))

(defn my-component []
  (uix/view
    [:<>
     [TyButton {:flavor "primary"} "Click Me"]
     [TyInput {:placeholder "Enter text..."}]]))

🔧 Peer Dependencies

Only React is required:

  • react >=18.0.0
  • react-dom >=18.0.0

Note: The core tyrell-components web components are loaded via CDN, not as an npm dependency!

🌍 Browser Support

Modern browsers with Web Components support:

  • Chrome 67+
  • Firefox 63+
  • Safari 13.1+
  • Edge 79+

📝 Why CDN Distribution?

Tyrell web components are framework-agnostic and designed for CDN distribution:

Zero version conflicts - One version serves all frameworks
Smaller bundles - Web components load once, shared across all React components
No build complexity - Just script tags
Better caching - CDN edge network
Framework freedom - Same components work in React, Vue, Svelte, vanilla JS

🤝 Contributing

This package is part of the Tyrell monorepo.

📄 License

MIT License - see LICENSE

🔗 Links