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.

  • breakpoint : the current breakpoint name (xs, sm, md, lg, xl, 2xl)
  • isSizeOrGreater : returns true if the viewport is at or above the given breakpoint
  • isSizeOrLess : returns 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.

  • xs: 0px
  • sm: 640px
  • md: 768px
  • lg: 1024px
  • xl: 1280px
  • 2xl: 1536px
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
};