Environment detection

Detect the platform, browser, and pointer type with tlenv and tlenvReactive.

import { Tldraw, TldrawUiButton, TldrawUiIcon, tlenv, tlenvReactive, useValue } from 'tldraw'
import 'tldraw/tldraw.css'
import './environment-detection.css'

function EnvironmentInfo() {
	// [1]
	const isCoarsePointer = useValue('coarse pointer', () => tlenvReactive.get().isCoarsePointer, [])

	// [2]
	const buttonSize = isCoarsePointer ? '48px' : '32px'

	return (
		<div className="tlui-menu environment-info">
			{/* [3] */}
			<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>
			<div>
				<strong>Pointer type (tlenvReactive):</strong> {isCoarsePointer ? 'Touch' : 'Mouse'}
			</div>
			<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>
	)
}

const components = {
	TopPanel: EnvironmentInfo,
}

export default function EnvironmentDetectionExample() {
	return (
		<div className="tldraw__editor">
			<Tldraw components={components} />
		</div>
	)
}

/*
[1]
`tlenvReactive` is an atom, so read it inside `useValue` to re-render when it changes. On a
touchscreen laptop `isCoarsePointer` flips as the user switches between touching the screen and
using the trackpad. `tlenv` is a plain object whose values are fixed at load time, so it can be
read directly during render.

[2]
Touch needs bigger targets than a mouse: 48px for a coarse pointer, 32px for a fine one.

[3]
`isIos` is checked before `isDarwin` because iPadOS reports itself as a Mac, so both are true on
an iPad.
*/

tldraw exports two objects that describe the environment it's running in:

  • tlenv is a plain object of flags that don't change during a session: isDarwin, isIos, isAndroid, isSafari, isFirefox, isChromeForIos, and so on. Read it directly, for example to show Cmd instead of Ctrl in shortcut hints on macOS.
  • tlenvReactive is an atom holding values that can change while the app is open, most importantly isCoarsePointer. Read it with useValue (or inside a tracked component) so your UI updates when it changes.

The panel at the top shows what each one reports for your device, and a button that grows to a 48px touch target when the pointer is coarse. On a touchscreen laptop, tap the screen and then move the trackpad to watch the pointer type and button size switch.

Is this page helpful?
Prev
Custom embeds
Next
Embed permissions