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

@btja/use-tailwind-media-query

v0.2.1

Published

Helper react hook to listen for tailwind media breakpoints. Intended for use with adaptive design.

Readme

use-tailwind-media-query

React 19 hooks for Tailwind-style breakpoints and arbitrary media queries.

Installation

npm i @btja/use-tailwind-media-query
yarn add @btja/use-tailwind-media-query
pnpm add @btja/use-tailwind-media-query
bun add @btja/use-tailwind-media-query

API

import {
  useTailwindBreakpoint,
  useIsDesktop,
  useIsLargeDesktop,
  useIsMobile,
  useIsTablet,
  useMediaQuery,
} from '@btja/use-tailwind-media-query'
import type { TailwindBreakpoint } from '@btja/use-tailwind-media-query'

Built-in tailwind breakpoints (TailwindBreakpoint):

| Breakpoint | Min width | | --- | --- | | sm | 640px | | md | 768px | | lg | 1024px | | xl | 1280px | | 2xl | 1536px |

Hooks:

| Hook | Returns | Purpose | | --- | --- | --- | | useMediaQuery(query: string, options?) | bool | Subscribe to any CSS media query | | useTailwindBreakpoint(tailwindBreakpoint: TailwindBreakpoint, options?) | bool | Tailwind breakpoint helper | | useIsMobile() | bool | true below md | | useIsTablet() | bool | true from md to below lg | | useIsDesktop() | bool | true at lg and above | | useIsLargeDesktop() | bool | true at xl and above |

Examples

useIsMobile (useIsTablet, useIsDesktop, useIsLargeDesktop):

import { useIsMobile } from '@btja/use-tailwind-media-query'

export function Example() {
  const isMobile = useIsMobile()

  return isMobile ? <MobileView /> : <DesktopView />
}

useTailwindBreakpoint:

import { useTailwindBreakpoint } from '@btja/use-tailwind-media-query'

export function SidebarLayout() {
  const showSidebar = useTailwindBreakpoint('lg')

  return showSidebar ? <DesktopLayout /> : <CompactLayout />
}
import { useTailwindBreakpoint } from '@btja/use-tailwind-media-query'

export function SsrSidebarLayout() {
  const showSidebar = useTailwindBreakpoint('lg', { noWindowDefault: true })

  return showSidebar ? <DesktopLayout /> : <CompactLayout />
}

useMediaQuery:

import { useMediaQuery } from '@btja/use-tailwind-media-query'

export function MotionPreference() {
  const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')

  return <p>{prefersReducedMotion ? 'Reduced motion' : 'Full motion'}</p>
}
import { useMediaQuery } from '@btja/use-tailwind-media-query'

export function SsrDesktopShell() {
  const isDesktop = useMediaQuery('(min-width: 1024px)', { noWindowDefault: true })

  return isDesktop ? <DesktopShell /> : <MobileShell />
}

SEO and SSR

If possible, keep both screen variants semantically equivalent for SEO, accessibility, and consistency purposes.

With this library, server render does not know the real viewport unless you provide an explicit fallback with noWindowDefault:

| Hook | Server-render value | | --- | --- | | useMediaQuery() | false | | useMediaQuery(query, { noWindowDefault: true }) | true | | useTailwindBreakpoint() | false | | useTailwindBreakpoint(breakpoint, { noWindowDefault: true }) | true | | useIsMobile() | true | | useIsDesktop() | false |

So this:

return useIsMobile() ? <MobileView /> : <DesktopView />

renders the mobile branch on the server, then hydrates to the real client viewport.

Use noWindowDefault when you want the server-rendered branch to default to a known assumption before the browser can evaluate matchMedia. This is useful when your SSR environment already has a strong hint about the likely viewport and you want to reduce layout flipping during hydration.

Ways to choose noWindowDefault on the server include:

  • infer a mobile/desktop default from the user-agent string
  • use device hints or request headers provided by your framework, CDN, or edge runtime
  • store a prior device preference in a cookie and reuse it during SSR
  • pick a product-level default such as mobile-first when no reliable hint exists

If the server guess is wrong, the client will correct to the real media-query result after hydration (with risk of layout flash).

If SEO matters, avoid putting unique primary content in only one branch. Keep headings, copy, links, and other canonical content the same when possible, and reserve screen- specific differences for layout or secondary UI.

Limitations

Current limitation: useMediaQuery depends on MediaQueryList.addEventListener / removeEventListener. Older Safari/WebView environments that only implement addListener / removeListener are not supported.

Motivation

Adaptive design

This hook is intended to be used in adaptive designs in React.

Use adaptive design when layouts should change across breakpoints (for example: dashboards, dense data views, and flows on desktop vs mobile). Prefer plain responsive design when the same hierarchy still works and only spacing, columns, or typography need to change.

Store

The hooks share a small media-query store so repeated breakpoint checks can reuse the same underlying subscription instead of attaching duplicate matchMedia listeners in every component.

Multiple components on the same screen can listen to the same media query without duplicating listeners and wasting memory.

Development

Using pnpm.

Setup:

npx skills add rolldown/tsdown # add tsdown agent skills
pnpm install

Useful commands:

pnpm run play # run vite playground
pnpm run test
pnpm run typecheck
pnpm run build

Before releasing (after npm login):

pnpm check:release
pnpm bump:version

npm publish --access public

AI Disclosure

Codex 5.4 was used to review and sanity-check the code. Codex was also used for:

  • Mockup UI in './playground' demo
  • JSDoc
  • Tests