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 🙏

© 2024 – Pkg Stats / Ryan Hefner

jotai-components

v0.2.0

Published

Jōtai utilities to reduce React boilerplate 👻⚛

Downloads

448

Readme

jotai-components 👻⚛

jotai-components allows you to pass Jotai atoms as props directly to React components, making your UI reactive without extra boilerplate:

const styleAtom = atom({ width: 100 })

// ✅ This component will not re-render
function Parent() {
  // ✅ `jotai.div` will re-render whenever styleAtom's value changes
  return <jotai.div $style={styleAtom}>example</jotai.div>
}

Installation

npm i jotai-components
yarn add jotai-components 
pnpm add jotai-components

The Problem

Sometimes, you need to use an atom's value within a component, but this leads to re-renders every time the atom’s value changes:

import { atom, useAtomValue } from 'jotai'

const styleAtom = atom({ width: 100 })

// ❌ This component re-renders every time the atom's value changes
function Parent() {
  const style = useAtomValue(styleAtom)
  
  return <div style={style}>example</div>
}

To prevent unnecessary re-renders in the parent component, you would normally move the atom logic to a child component:

import { type Atom, atom, useAtomValue } from 'jotai'
import {
  type CSSProperties,
  type HTMLAttributes,
  type PropsWithChildren,
  type ReactNode,
  forwardRef,
} from 'react'

const styleAtom = atom({ width: 100 })

// ✅ This component will not re-render
function Parent() {
  return <Child styleAtom={styleAtom}>example</Child>
}

// 😕 Based on requirements, this can quickly become cumbersome (imports are also inflated)
const Child = forwardRef<
  HTMLDivElement,
  Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {
  styleAtom: Atom<CSSProperties>
}
>(function Child(props, ref) {
  return <div {...props} ref={ref} />
})

While this pattern prevents unnecessary re-renders, it can become cumbersome as the number of atoms grows, and it increases the complexity of the component hierarchy.

The Solution

With jotai-components, you can pass atoms directly as props to components, without needing to create separate child components for each atom:

import { atom } from 'jotai'
import { jotai } from 'jotai-components'

const styleAtom = atom({ width: 100 })

// 🤩 Parent does not re-render, and atoms can be passed directly as props
function Parent() {
  return (
    <jotai.div className="ordinary-props-as-well" $style={styleAtom}>
      example
    </jotai.div>
  )
}

This solution simplifies the code by removing the need for useAtomValue and separate child components.

API

jotai.<tag>

Use jotai.<tag> to create components (e.g., jotai.span, jotai.button, etc.) that accept props either as regular values or as atoms (see the example above).

jotai.memo

Use jotai.memo to render dynamic content from atoms. It accepts atoms as children that resolve to values conforming to the ReactNode type:

import { atom } from 'jotai'
import { jotai } from 'jotai-components'

const worldAtom = atom('World')

function Parent() {
  return (
    <div>
      {/* Hello, World! */}
      Hello, <jotai.memo>{worldAtom}</jotai.memo>!
    </div>
  )
}

jotai.create

You can create reactive components around custom React components:

import { atom } from 'jotai'
import { jotai } from 'jotai-components'
import { motion } from 'framer-motion'
import { Button } from 'react-aria-components'

const JotaiMotionButton = jotai.create(motion.create(Button))

const motionStyleAtom = atom({ width: 100 })

function Parent() {
  return (
    <JotaiMotionButton transition={{ duration: 1 }} $animate={motionStyleAtom}>
      example
    </JotaiMotionButton>
  )
}

[!CAUTION]
Avoid using jotai.create inside render loops, as this will create a new component on every render. Instead, move the jotai.create call outside the component or use techniques to ensure a stable reference.

[!WARNING]
While this example uses Framer Motion with atoms to update the animate prop, which can trigger re-renders, it may not be suitable for continuous animation values. In those cases, consider using MotionValues.

License

MIT