Skip to main content
Beta FeatureThis feature is currently in beta, and needs to be imported from @servicetitan/anvil2/beta.While we hope to minimize breaking changes, they may occur due to feedback we receive or other improvements. These will always be documented in the changelog and communicated in Slack.Please reach out in the #ask-designsystem channel with any questions or feedback!
A React hook for detecting whether to use mobile or desktop UI patterns based on device characteristics.

Overview

useAdaptiveView determines the optimal UI pattern (e.g., Dialog vs Popover) by combining two factors:
  • Screen size: Viewport width relative to the md breakpoint (768px)
  • Pointer precision: Whether the device has a coarse (touch) or fine (mouse/trackpad) primary pointer
A device is considered “mobile” only when it has both a small screen (below 768px) and a coarse pointer (touch input). This approach correctly distinguishes between:
Device TypeScreen SizePointerResult
Mobile phonesSmallCoarse (touch)Mobile view
iPads/tabletsLargeCoarse (touch)Desktop view
Touch laptopsLargeFine (trackpad)Desktop view
DesktopLargeFine (mouse)Desktop view

Import

import { useAdaptiveView } from "@servicetitan/anvil2/beta";

Return Value

The hook returns an object with the following properties:
PropertyTypeDescription
view"mobile" | "desktop"The current adaptive view type
isMobilebooleantrue when the device should use mobile-optimized UI
isDesktopbooleantrue when the device should use desktop-optimized UI

Basic Usage

Use useAdaptiveView to conditionally render different UI components based on the device:
import { useAdaptiveView } from "@servicetitan/anvil2/beta";
import { Dialog, Popover, Button } from "@servicetitan/anvil2";

function AdaptiveMenu({ children }) {
  const { isMobile } = useAdaptiveView();

  return isMobile ? (
    <Dialog>
      <Dialog.Trigger>
        <Button>Open Menu</Button>
      </Dialog.Trigger>
      <Dialog.Content>{children}</Dialog.Content>
    </Dialog>
  ) : (
    <Popover>
      <Popover.Trigger>
        <Button>Open Menu</Button>
      </Popover.Trigger>
      <Popover.Content>{children}</Popover.Content>
    </Popover>
  );
}

Use Cases

Adaptive Selection Components

Components like SelectField use this hook internally to render a full-screen dialog on mobile devices and a popover dropdown on desktop:
const { isMobile } = useAdaptiveView();

// On mobile: render a tray-style Dialog for easier touch interaction
// On desktop: render a Popover positioned below the trigger

Form Input Optimization

Adjust form interactions based on device capabilities:
function AdaptiveForm() {
  const { isMobile } = useAdaptiveView();

  return (
    <Form>
      {isMobile ? (
        // Full-screen form with larger touch targets
        <MobileFormLayout />
      ) : (
        // Standard form layout
        <DesktopFormLayout />
      )}
    </Form>
  );
}

Technical Details

Detection Method

The hook uses two browser APIs to detect device characteristics:
  1. useBreakpoint: Monitors viewport width against Anvil2 breakpoint tokens
  2. CSS Media Query: Uses (pointer: coarse) to detect touch-primary devices

Dynamic Updates

The hook automatically updates when:
  • The viewport is resized (e.g., browser window resized, device rotation)
  • The pointer capability changes (e.g., connecting/disconnecting a mouse on a touch device)

SSR Compatibility

During server-side rendering, the hook defaults to desktop view since browser APIs are unavailable. The correct view is determined on the client after hydration.
Last modified on January 23, 2026