Environment detection with tlenv and tlenvReactive
This example demonstrates tldraw's environment detection APIs for building platform-aware and device-adaptive interfaces:
Static detection (tlenv):
- Platform detection (macOS, Windows, iOS, Android)
- Browser detection (Safari, Firefox, Chrome)
- Platform-specific keyboard shortcuts (Cmd vs Ctrl)
Reactive detection (tlenvReactive with useValue):
- Pointer type detection (coarse/fine) - can change mid-session on hybrid devices
- Real-time updates when switching between touch and mouse input
The example shows practical usage like adapting button sizes based on pointer type - larger touch targets (48px) for coarse pointers (touch) and smaller targets (32px) for fine pointers (mouse/trackpad).
Use tlenv for static properties that don't change during the session, and tlenvReactive with the useValue hook for properties that can change reactively, especially isCoarsePointer on touchscreen laptops.
import { Tldraw, TldrawUiButton, TldrawUiIcon, tlenv, tlenvReactive, useValue } from 'tldraw'
import 'tldraw/tldraw.css'
import './environment-detection.css'
// [1]
function EnvironmentInfo() {
// [2]
const isCoarsePointer = useValue('coarse pointer', () => tlenvReactive.get().isCoarsePointer, [
tlenvReactive,
])
// [3]
const buttonSize = isCoarsePointer ? '48px' : '32px'
return (
<div className="tlui-menu environment-info">
{/* [4] Static detection with tlenv */}
<div>
<strong>Platform (tlenv):</strong> {tlenv.isIos && 'iOS'}
{tlenv.isDarwin && !tlenv.isIos && 'macOS'}
{tlenv.isAndroid && 'Android'}
{!tlenv.isDarwin && !tlenv.isIos && !tlenv.isAndroid && 'Other'}
</div>
<div>
<strong>Browser:</strong> {tlenv.isSafari && 'Safari'}
{tlenv.isFirefox && 'Firefox'}
{tlenv.isChromeForIos && 'Chrome for iOS'}
{!tlenv.isSafari && !tlenv.isFirefox && !tlenv.isChromeForIos && 'Other'}
</div>
<div>
<strong>Modifier key:</strong> {tlenv.isDarwin ? '⌘ Cmd' : 'Ctrl'}
</div>
{/* [5] Reactive detection with tlenvReactive */}
<div>
<strong>Pointer type (reactive):</strong> {isCoarsePointer ? 'Touch' : 'Mouse'}
</div>
{/* [6] Adaptive button based on pointer type */}
<TldrawUiButton
type="normal"
style={{
width: buttonSize,
height: buttonSize,
border: '1px solid var(--tl-color-text-3)',
}}
onClick={() => alert(`Button size: ${buttonSize}`)}
>
<TldrawUiIcon icon="dot" label="Dot" />
</TldrawUiButton>
</div>
)
}
export default function RequestEnvironmentDetectionWithTlenvAndTlenvreactiveExample() {
return (
<div className="tldraw__editor">
<Tldraw
components={{
// [7]
TopPanel: EnvironmentInfo,
}}
/>
</div>
)
}
/*
This example demonstrates environment detection using tlenv and tlenvReactive.
[1] Component that displays environment information
[2] Subscribe to tlenvReactive using useValue hook. Since tlenvReactive is an Atom,
call .get() to access its value. The isCoarsePointer property updates when users
switch between touch and mouse input.
[3] Calculate button size based on pointer type - touch needs larger targets (48px)
while mouse can use smaller buttons (32px)
[4] Display static environment detection with tlenv - platform (isDarwin, isIos, isAndroid)
and browser (isSafari, isFirefox, isChromeForIos). Note: isIos is checked before isDarwin
because iPadOS reports as Mac, making both isDarwin and isIos true on iPads.
[5] Display reactive pointer detection that updates when input method changes
[6] Adaptive button that changes size based on pointer type
[7] Render component in front of canvas using InFrontOfTheCanvas slot
Key differences:
- tlenv: Static object, direct property access (tlenv.isDarwin)
- tlenvReactive: Reactive Atom, requires useValue(() => tlenvReactive.get().property, [tlenvReactive])
*/
Is this page helpful?
Prev
Custom stroke and font sizesNext
Deep links