AbsoluteJS

React Hooks

Responsive design utilities for AbsoluteJS React applications. SSR-aware viewport breakpoints with user-agent detection for zero-layout-shift server rendering.

#useMediaQuery

The useMediaQuery hook provides viewport-based responsive breakpoints. It returns the current breakpoint name and two helper functions for checking size ranges.

breakpointthe current breakpoint name (xs, sm, md, lg, xl, 2xl)
isSizeOrGreaterreturns true if the viewport is at or above the given breakpoint
isSizeOrLessreturns true if the viewport is at or below the given breakpoint
TSX
import { useMediaQuery } from '@absolutejs/absolute/react/hooks';

export const ResponsiveLayout = () => {
  const { breakpoint, isSizeOrGreater, isSizeOrLess } = useMediaQuery();

  return (
    <div>
      <p>Current breakpoint: {breakpoint}</p>

      {isSizeOrGreater('lg') && (
        <aside>Desktop sidebar: only visible on lg and above</aside>
      )}

      {isSizeOrLess('sm') && (
        <nav>Mobile bottom nav: only visible on sm and below</nav>
      )}
    </div>
  );
};

#UserAgentProvider

The UserAgentProvider is an SSR-aware context provider that passes the user-agent string to useMediaQuery. On the server, the hook reads the user-agent to infer whether the client is mobile or desktop and returns a sensible default breakpoint. On the client, it switches to real viewport measurement via matchMedia.

TSX
// In your page component or layout
import { UserAgentProvider } from '@absolutejs/absolute/react/hooks';

export const App = ({ userAgent, children }: {
  userAgent: string;
  children: React.ReactNode;
}) => (
  <UserAgentProvider userAgent={userAgent}>
    {children}
  </UserAgentProvider>
);

// AbsoluteJS page handlers pass the user-agent automatically:
import { reactHandler } from '@absolutejs/absolute';

app.get('/', reactHandler({
  component: 'HomePage',
  head: { title: 'Home' },
  props: async ({ request }) => {
    return { userAgent: request.headers.get('user-agent') ?? '' };
  }
}));

#Breakpoints

The default breakpoints match Tailwind CSS values. The hook checks viewport width against these thresholds and returns the largest breakpoint that the current width satisfies.

xs0px
sm640px
md768px
lg1024px
xl1280px
2xl1536px
TSX
// Default breakpoints (matches Tailwind CSS)
// xs: 0px
// sm: 640px
// md: 768px
// lg: 1024px
// xl: 1280px
// 2xl: 1536px

// The hook returns the current breakpoint name based on
// viewport width. On the server, it infers a reasonable
// default from the user-agent string (mobile vs desktop).

import { useMediaQuery } from '@absolutejs/absolute/react/hooks';

export const AdaptiveGrid = () => {
  const { breakpoint, isSizeOrGreater } = useMediaQuery();

  const columns = isSizeOrGreater('xl') ? 4
    : isSizeOrGreater('lg') ? 3
    : isSizeOrGreater('md') ? 2
    : 1;

  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: `repeat(${columns}, 1fr)`,
      gap: '1rem'
    }}>
      {/* grid items */}
    </div>
  );
};

#SSR Usage

When rendering on the server, there is no window object to measure. The UserAgentProvider solves this by parsing the user-agent string to detect mobile devices. Mobile user-agents get sm as the default breakpoint, and desktop user-agents get lg. This means the server renders the correct layout variant on the first pass, avoiding layout shift when the client hydrates.

TSX
// SSR-aware responsive rendering
// On the server, useMediaQuery reads the user-agent to detect
// mobile vs desktop and returns a sensible default breakpoint.
// On the client, it switches to real viewport measurement.

import { useMediaQuery } from '@absolutejs/absolute/react/hooks';
import { UserAgentProvider } from '@absolutejs/absolute/react/hooks';

export const Page = ({ userAgent }: { userAgent: string }) => (
  <UserAgentProvider userAgent={userAgent}>
    <Layout />
  </UserAgentProvider>
);

const Layout = () => {
  const { isSizeOrGreater, isSizeOrLess } = useMediaQuery();

  return (
    <div>
      {/* Server renders the correct variant: no layout shift */}
      {isSizeOrGreater('md') ? (
        <DesktopNav />
      ) : (
        <MobileNav />
      )}

      <main>
        {isSizeOrLess('sm') && <MobileBanner />}
        <Content />
      </main>
    </div>
  );
};

#API Reference

TS
// useMediaQuery return type
type UseMediaQueryResult = {
  // Current breakpoint name: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
  breakpoint: string;

  // Returns true if the viewport is at or above the given breakpoint
  isSizeOrGreater: (size: Breakpoint) => boolean;

  // Returns true if the viewport is at or below the given breakpoint
  isSizeOrLess: (size: Breakpoint) => boolean;
};

type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';

// Default breakpoint values
const defaultBreakpoints: Record<Breakpoint, number> = {
  'xs': 0,
  'sm': 640,
  'md': 768,
  'lg': 1024,
  'xl': 1280,
  '2xl': 1536
};