iOS Zoom Lock

Stops iOS from zooming into small text fields on focus.

Installation

pnpm dlx shadcn@latest add @paneui/ios-zoom-lock

Usage

Examples

Scoped

Why

Fields keep the 13px control text on phones. iOS and iPadOS Safari zoom into a focused field whose text is under 16px and stay zoomed after it loses focus. This is the recommended fix when that zoom gets in the way; max-md:text-base on a field is the alternative when you'd rather have 16px text on phones.

How it works

When an input, textarea, select or contenteditable element gets focus, the viewport meta gains maximum-scale=1 (an existing maximum-scale is replaced). When focus leaves text entry, the meta gets its original content back exactly. Moving from one field to the next keeps the lock, so the meta doesn't flicker. Buttons, checkboxes, radios, ranges, file and colour inputs don't lock.

Pinch zoom

Outside a focused field the viewport is untouched, so pinch zoom works as before. Safari still allows pinch zoom while a field is focused, since it ignores maximum-scale for gestures; a home-screen web app honours it, so there pinch zoom waits until the field loses focus.

Devices

Off by default and imported by nothing else. It runs only on iPhone, iPad and iPod, including iPads that report a desktop Mac user agent (MacIntel with more than one touch point). devices="touch" adds every coarse pointer; other browsers don't zoom on focus, so this is rarely needed.

Edge cases

Everything runs in effects, so server rendering is unaffected. Without a viewport meta, a temporary one is added while a field is focused and removed afterwards. Several mounted locks share one listener set, and the original content comes back when the last one unmounts or its enabled turns off, focused or not. A field removed while focused fires no blur; the next touch restores the meta.

API Reference

IosZoomLock

Runs useIosInputZoomLock and renders nothing. Mount it once in a layout, a form or a sheet.

enabled= true
boolean
Turns the lock off without unmounting.
devices= "ios"
"ios" | "touch"
touch also locks on any coarse pointer.
PropTypeDefaultDescription
enabledbooleantrueTurns the lock off without unmounting.
devices"ios" | "touch""ios"touch also locks on any coarse pointer.

useIosInputZoomLock

useIosInputZoomLock({ enabled, devices }) with the same options, for a component that already owns the state.

isIosDevice

True on iPhone, iPad and iPod, including iPadOS with a desktop user agent. Call it in an effect.

withMaximumScale

Returns viewport content with maximum-scale=1, keeping the other directives in order.