LOKE Design System
Hooks

useIsMobile

Detect whether the current viewport is “mobile” (by default, width < 768px) using a media query, and adapt your UI behavior accordingly.

useIsMobile

useIsMobile returns a boolean that indicates whether the current viewport width is below the mobile breakpoint (defaults to 768px). It listens for viewport size changes using the matchMedia API and updates reactively.

  • true → viewport is narrower than 768px
  • false → viewport is 768px or wider

This hook is a small convenience around window.matchMedia. It’s ideal for toggling UI or behavior between compact and desktop layouts without wiring up media listeners manually.


API

function useIsMobile(): boolean;

Prop

Type


Usage

1) Conditional rendering (mobile vs. desktop navigation)

import * as React from 'react';
import { useIsMobile } from '@loke/design-system/use-mobile';

function AppHeader() {
  const isMobile = useIsMobile();

  return (
    <header className="border-b bg-card">
      {isMobile ? <MobileNav /> : <DesktopNav />}
    </header>
  );
}

2) Adapting behavior (e.g., default open state)

import * as React from 'react';
import { useIsMobile } from '@loke/design-system/use-mobile';

export function SidebarContainer() {
  const isMobile = useIsMobile();
  // Sidebar closed by default on mobile; open on desktop
  const [open, setOpen] = React.useState(!isMobile);

  React.useEffect(() => {
    setOpen(!isMobile);
  }, [isMobile]);

  return (
    <aside className={open ? 'block' : 'hidden md:block'}>
      <Sidebar />
    </aside>
  );
}

3) Show compact controls for small viewports

import { Button } from '@loke/design-system/button';
import { useIsMobile } from '@loke/design-system/use-mobile';

function ActionBar() {
  const isMobile = useIsMobile();

  return (
    <div className="flex items-center gap-2">
      <Button size={isMobile ? 'sm' : 'md'}>Primary</Button>
      <Button size={isMobile ? 'sm' : 'md'} variant="outline">
        Secondary
      </Button>
    </div>
  );
}

SSR and first-render behavior

  • This hook uses browser APIs (matchMedia, window.innerWidth) and should run in Client Components (e.g., with "use client" in Next.js).
  • Internally, it initializes to undefined and then sets the boolean after mounting; the returned value is coerced to false until the first effect runs.
    • If avoiding a brief “desktop” render on mobile is important, gate the UI until the first measurement completes or render layout styles responsively with CSS and use the hook only for behavior.

Example: wait until hydrated value is known

function ResponsivePanel() {
  const isMobile = useIsMobile();
  const [ready, setReady] = React.useState(false);

  React.useEffect(() => setReady(true), []);

  if (!ready) return null;
  return isMobile ? <MobilePanel /> : <DesktopPanel />;
}

For purely visual layout differences, prefer CSS media queries (e.g., Tailwind responsive classes). Use this hook when your logic/behavior depends on viewport size.


Custom breakpoint (optional pattern)

The built-in hook uses a fixed 768px breakpoint. If you need a different threshold, you can build a small wrapper:

import * as React from 'react';

function useMaxWidth(maxWidthPx: number) {
  const [match, setMatch] = React.useState<boolean | undefined>(undefined);

  React.useEffect(() => {
    const mql = window.matchMedia(`(max-width: ${maxWidthPx - 1}px)`);
    const onChange = () => setMatch(window.innerWidth < maxWidthPx);

    mql.addEventListener('change', onChange);
    onChange(); // initialize
    return () => mql.removeEventListener('change', onChange);
  }, [maxWidthPx]);

  return !!match;
}

// Example:
function Toolbar() {
  const compact = useMaxWidth(1024); // treat <1024px as compact
  return <div className={compact ? 'gap-1' : 'gap-3'}>…</div>;
}

Tips

  • Prefer this hook for behavioral toggles (state defaults, feature flags) and use CSS for visual layout changes whenever possible.
  • Combine with other responsive utilities (e.g., Columns, Stack, or Tailwind’s sm:, md:, lg: modifiers) for a clean, scalable approach.
  • If you need to support server-only rendering for a specific code path, guard window/document access or defer rendering until mounted.

Import

import { useIsMobile } from '@loke/design-system/use-mobile';