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

@pyreon/attrs

v0.12.10

Published

Attrs HOC chaining for Pyreon components

Downloads

3,664

Readme

@pyreon/attrs

Immutable, chainable default-props factory for Pyreon components.

Think of styled-components' .attrs() as a standalone, type-safe composition system. Define default props, swap base components, attach HOCs, and add metadata — all through an immutable chain where every call returns a new component.

Features

  • Immutable chaining — every method returns a new component, never mutates the original
  • Props merge orderpriorityAttrs > attrs > explicit props, with full control over precedence
  • Prop filtering — strip internal props before they reach the DOM
  • HOC composition — named HOCs via .compose() with selective removal
  • Static metadata — attach and access custom data via .statics() / .meta
  • TypeScript inference — generics accumulate through the chain

Installation

bun add @pyreon/attrs

Quick Start

import attrs from '@pyreon/attrs'
import { Element } from '@pyreon/elements'

const Button = attrs({ name: 'Button', component: Element }).attrs({
  tag: 'button',
  alignX: 'center',
  alignY: 'center',
})

// Renders Element with tag="button", alignX="center", alignY="center"
Button({ label: 'Click me' })

// Explicit props override attrs defaults
Button({ tag: 'a', label: 'Link button' })

API

attrs(options)

Creates an attrs-enhanced component.

const Component = attrs({
  name: 'ComponentName', // required — sets displayName
  component: BaseComponent, // required — the Pyreon component to wrap
})

.attrs(props | callback, options?)

Add default props. Can be called multiple times — defaults stack left-to-right.

// Object form — static defaults
Button.attrs({ tag: 'button' })

// Callback form — computed defaults based on current props
Button.attrs((props) => ({
  'aria-label': props.label,
}))

// Priority — resolved before regular attrs, cannot be overridden by explicit props
Button.attrs({ tag: 'button' }, { priority: true })

// Filter — remove props before passing to the underlying component
Button.attrs({}, { filter: ['internalFlag', 'variant'] })

Props merge order:

priorityAttrs (highest) → attrs → explicit props (lowest for priority, highest for regular)

For regular attrs, explicit props win. For priority attrs, the priority value wins.

.config(options)

Reconfigure the component. Returns a new component instance.

// Rename
Button.config({ name: 'PrimaryButton' })

// Swap the base component
Button.config({ component: AnotherComponent })

// Enable debug mode — adds data-attrs attribute in development
Button.config({ DEBUG: true })

.compose(hocs)

Attach named Higher-Order Components. Applied in declaration order.

const Enhanced = Button.compose({
  withTheme: (Component) => (props) => Component({ ...props, themed: true }),
  withTracking: trackingHoc,
})

// Remove a specific HOC from the chain
const WithoutTracking = Enhanced.compose({ withTracking: null })

.statics(metadata)

Attach metadata accessible via the .meta property.

const Button = attrs({ name: 'Button', component: Element }).statics({
  category: 'action',
  sizes: ['sm', 'md', 'lg'],
})

Button.meta.category // => 'action'
Button.meta.sizes // => ['sm', 'md', 'lg']

isAttrsComponent(value)

Runtime type guard.

import { isAttrsComponent } from '@pyreon/attrs'

isAttrsComponent(Button) // => true
isAttrsComponent('div') // => false

getDefaultAttrs()

Retrieve the computed default props for a component.

Button.getDefaultAttrs() // => { tag: 'button', alignX: 'center', ... }

TypeScript

Each .attrs<P>() call adds P to the component's prop types through MergeTypes:

const Base = attrs({ name: 'Base', component: Element })

const Typed = Base.attrs<{ variant: 'primary' | 'secondary' }>({ variant: 'primary' }).attrs<{
  size?: 'sm' | 'md' | 'lg'
}>({})

// Typed accepts: Element props + { variant, size? }
Typed({ variant: 'secondary', size: 'lg', label: 'Hello' })

Access the accumulated types via type-only properties:

type AllProps = typeof Typed.$$types
type OriginalProps = typeof Typed.$$originTypes
type ExtendedProps = typeof Typed.$$extendedTypes

Peer Dependencies

| Package | Version | | --------------- | -------- | | @pyreon/core | >= 0.0.1 | | @pyreon/ui-core | >= 0.0.1 |

License

MIT