Universal style library for React and React Native with cross-platform support
—
React Native-specific enhancements including layout measurement, pressability handling, platform optimizations, and cross-platform compatibility features.
Element layout measurement system providing React Native-style layout information on both platforms.
/**
* Hook for tracking element layout changes
* @param stateRef - Component state reference
* @param onLayout - Layout change callback
* @returns Layout measurement utilities
*/
function useElementLayout(
stateRef?: React.MutableRefObject<any>,
onLayout?: (event: LayoutEvent) => void
): LayoutMeasurement;
/**
* Set the layout measurement strategy
* @param strategy - Measurement strategy to use
*/
function setOnLayoutStrategy(strategy: LayoutStrategy): void;
/**
* Create element measurement function
* @param element - Element to measure
* @returns Measurement function
*/
function createMeasure(element: Element): MeasureFunction;
/**
* Create window-relative measurement function
* @param element - Element to measure
* @returns Window measurement function
*/
function createMeasureInWindow(element: Element): MeasureInWindowFunction;
/**
* Create layout measurement function
* @param element - Element to measure
* @returns Layout measurement function
*/
function createMeasureLayout(element: Element): MeasureLayoutFunction;
interface LayoutEvent {
nativeEvent: {
layout: {
x: number;
y: number;
width: number;
height: number;
};
};
}
type LayoutStrategy = 'native' | 'web';
interface LayoutMeasurement {
x: number;
y: number;
width: number;
height: number;
}
type MeasureFunction = (callback: (x: number, y: number, width: number, height: number, pageX: number, pageY: number) => void) => void;
type MeasureInWindowFunction = (callback: (x: number, y: number, width: number, height: number) => void) => void;
type MeasureLayoutFunction = (relativeTo: Element, callback: (left: number, top: number, width: number, height: number) => void) => void;Usage Examples:
import { useElementLayout, View, Text } from "@tamagui/core";
function MeasuredComponent() {
const [layout, setLayout] = useState(null);
const handleLayout = (event: LayoutEvent) => {
const { x, y, width, height } = event.nativeEvent.layout;
setLayout({ x, y, width, height });
};
useElementLayout(undefined, handleLayout);
return (
<View onLayout={handleLayout}>
<Text>
{layout && `Size: ${layout.width}x${layout.height}`}
</Text>
</View>
);
}
// Manual measurement
function ManualMeasurement() {
const elementRef = useRef(null);
const measureElement = () => {
if (elementRef.current?.measure) {
elementRef.current.measure((x, y, width, height, pageX, pageY) => {
console.log('Element measurements:', { x, y, width, height, pageX, pageY });
});
}
};
return (
<View ref={elementRef}>
<button onClick={measureElement}>Measure Me</button>
</View>
);
}React Native pressability system for handling touch interactions with smooth press states.
/**
* Hook for React Native-style pressability (native platform only)
* @param events - Event handlers for press interactions
* @returns Pressability props to spread on components
*/
function usePressability(events?: PressabilityEvents): PressabilityConfig;
interface PressabilityEvents {
/** Called when press begins */
onPressIn?: (event: PressEvent) => void;
/** Called when press ends */
onPressOut?: (event: PressEvent) => void;
/** Called when press completes */
onPress?: (event: PressEvent) => void;
/** Called when press is cancelled */
onPressCancel?: (event: PressEvent) => void;
/** Called when long press is detected */
onLongPress?: (event: PressEvent) => void;
/** Hit area expansion */
hitSlop?: HitSlop;
/** Delay before onPressIn */
delayPressIn?: number;
/** Delay before onPressOut */
delayPressOut?: number;
/** Delay before onLongPress */
delayLongPress?: number;
/** Minimum press duration */
minPressDuration?: number;
}
interface PressabilityConfig {
onPressIn?: (event: PressEvent) => void;
onPressOut?: (event: PressEvent) => void;
onResponderGrant?: (event: ResponderEvent) => void;
onResponderMove?: (event: ResponderEvent) => void;
onResponderRelease?: (event: ResponderEvent) => void;
onResponderTerminate?: (event: ResponderEvent) => void;
onStartShouldSetResponder?: () => boolean;
onMoveShouldSetResponder?: () => boolean;
}
interface PressEvent {
nativeEvent: {
locationX: number;
locationY: number;
pageX: number;
pageY: number;
target: any;
timestamp: number;
};
}
interface HitSlop {
top?: number;
left?: number;
bottom?: number;
right?: number;
}Usage Examples:
import { usePressability, View, Text } from "@tamagui/core";
function PressableButton({ onPress, children }) {
const pressability = usePressability({
onPress,
onPressIn: () => console.log('Press started'),
onPressOut: () => console.log('Press ended'),
onLongPress: () => console.log('Long press detected'),
hitSlop: { top: 10, left: 10, bottom: 10, right: 10 },
delayLongPress: 500,
});
return (
<View
{...pressability}
style={{
padding: 16,
backgroundColor: '#007AFF',
borderRadius: 8,
}}
>
<Text style={{ color: 'white', textAlign: 'center' }}>
{children}
</Text>
</View>
);
}React Native responder event system for advanced touch handling.
/**
* Hook for React Native responder events (web platform)
* @param stateRef - Component state reference
* @param props - Component props with responder events
*/
function useResponderEvents(
stateRef: React.MutableRefObject<any>,
props?: ResponderEventProps
): void;
interface ResponderEventProps {
/** Should respond to move events */
onMoveShouldSetResponder?: (event: ResponderEvent) => boolean;
/** Should respond to move events (capture phase) */
onMoveShouldSetResponderCapture?: (event: ResponderEvent) => boolean;
/** Responder granted */
onResponderGrant?: (event: ResponderEvent) => void;
/** Responder rejected */
onResponderReject?: (event: ResponderEvent) => void;
/** Responder started */
onResponderStart?: (event: ResponderEvent) => void;
/** Responder moved */
onResponderMove?: (event: ResponderEvent) => void;
/** Responder ended */
onResponderEnd?: (event: ResponderEvent) => void;
/** Responder released */
onResponderRelease?: (event: ResponderEvent) => void;
/** Responder terminated */
onResponderTerminate?: (event: ResponderEvent) => void;
/** Responder termination requested */
onResponderTerminationRequest?: (event: ResponderEvent) => boolean;
/** Should respond to start events */
onStartShouldSetResponder?: (event: ResponderEvent) => boolean;
/** Should respond to start events (capture phase) */
onStartShouldSetResponderCapture?: (event: ResponderEvent) => boolean;
/** Should respond to scroll events */
onScrollShouldSetResponder?: (event: ResponderEvent) => boolean;
/** Should respond to scroll events (capture phase) */
onScrollShouldSetResponderCapture?: (event: ResponderEvent) => boolean;
/** Should respond to selection change events */
onSelectionChangeShouldSetResponder?: (event: ResponderEvent) => boolean;
/** Should respond to selection change events (capture phase) */
onSelectionChangeShouldSetResponderCapture?: (event: ResponderEvent) => boolean;
}
interface ResponderEvent {
nativeEvent: {
changedTouches: Touch[];
identifier: number;
locationX: number;
locationY: number;
pageX: number;
pageY: number;
target: any;
timestamp: number;
touches: Touch[];
};
}Functions and components for React Native performance optimizations.
/**
* Create optimized view for React Native platform
* @param children - Child components
* @param props - View props
* @param baseViews - Base React Native components
* @returns Optimized React Native view
*/
function createOptimizedView(
children: React.ReactNode,
props: Record<string, any>,
baseViews: BaseViews
): React.ReactElement;
/**
* Get base React Native components
* @returns Object with React Native base components
*/
function getBaseViews(): BaseViews;
/**
* Add React Native version-specific valid styles
*/
function addNativeValidStyles(): void;
interface BaseViews {
View: React.ComponentType<any>;
Text: React.ComponentType<any>;
StyleSheet: any;
TextAncestor: React.ComponentType<any> | undefined;
Pressable: React.ComponentType<any>;
}Style injection utilities for development mode (web platform).
/**
* Inject CSS styles dynamically for development mode
* @param options - Injection options
*/
function injectStyles(options: InjectStylesOptions): void;
interface InjectStylesOptions {
/** File path for CSS identification */
filePath: string;
/** CSS string to inject */
css: string;
}Usage Examples:
import { injectStyles } from "@tamagui/core";
// Development-time style injection
if (process.env.NODE_ENV === 'development') {
injectStyles({
filePath: 'components/Button.tsx',
css: `
.debug-button {
border: 2px solid red !important;
}
`,
});
}Enhanced components with full React Native prop support.
/**
* Enhanced View component with React Native props
*/
declare const View: TamaguiComponent<
TamaDefer,
TamaguiElement,
RNTamaguiViewNonStyleProps,
StackStyleBase,
{}
>;
/**
* Enhanced Text component with React Native text props
*/
declare const Text: TamaguiComponent<
TamaDefer,
TamaguiTextElement,
RNTamaguiTextNonStyleProps,
TextStylePropsBase,
{}
>;
interface RNTamaguiViewNonStyleProps extends StackNonStyleProps {
// React Native View props
/** Accessibility properties */
accessible?: boolean;
accessibilityActions?: AccessibilityAction[];
accessibilityLabel?: string;
accessibilityLabelledBy?: string;
accessibilityRole?: AccessibilityRole;
accessibilityState?: AccessibilityState;
accessibilityValue?: AccessibilityValue;
accessibilityHint?: string;
accessibilityLanguage?: string;
/** Android-specific props */
collapsable?: boolean;
focusable?: boolean;
/** Layout and interaction */
onLayout?: (event: LayoutEvent) => void;
onStartShouldSetResponder?: (event: ResponderEvent) => boolean;
onMoveShouldSetResponder?: (event: ResponderEvent) => boolean;
onResponderGrant?: (event: ResponderEvent) => void;
onResponderMove?: (event: ResponderEvent) => void;
onResponderRelease?: (event: ResponderEvent) => void;
onResponderTerminate?: (event: ResponderEvent) => void;
onResponderTerminationRequest?: (event: ResponderEvent) => boolean;
/** Pointer events */
pointerEvents?: 'auto' | 'none' | 'box-none' | 'box-only';
/** Test IDs */
testID?: string;
nativeID?: string;
/** Hit testing */
hitSlop?: HitSlop;
/** Remove clipped subviews (performance) */
removeClippedSubviews?: boolean;
}
interface RNTamaguiTextNonStyleProps extends TextNonStyleProps {
// React Native Text props
/** Text selection */
selectable?: boolean;
selectionColor?: string;
/** Text styling */
allowFontScaling?: boolean;
maxFontSizeMultiplier?: number;
minimumFontScale?: number;
suppressHighlighting?: boolean;
/** Text measurement */
adjustsFontSizeToFit?: boolean;
numberOfLines?: number;
ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip';
/** Text events */
onTextLayout?: (event: TextLayoutEvent) => void;
onPress?: (event: PressEvent) => void;
onPressIn?: (event: PressEvent) => void;
onPressOut?: (event: PressEvent) => void;
onLongPress?: (event: PressEvent) => void;
/** Accessibility */
accessible?: boolean;
accessibilityLabel?: string;
accessibilityRole?: AccessibilityRole;
accessibilityState?: AccessibilityState;
}Constants and utilities for platform-specific behavior.
/** Web platform detection */
declare const isWeb: boolean;
/** Server environment detection */
declare const isServer: boolean;
/** Client environment detection */
declare const isClient: boolean;
/** Android platform detection */
declare const isAndroid: boolean;
/** iOS platform detection */
declare const isIos: boolean;
/** Touch capability detection */
declare const isTouchable: boolean;
/** Web touch capability detection */
declare const isWebTouchable: boolean;
/**
* SSR-safe useLayoutEffect hook
*/
declare const useIsomorphicLayoutEffect: typeof React.useLayoutEffect;Usage Examples:
import { isWeb, isAndroid, isIos, View, Text } from "@tamagui/core";
function PlatformSpecificComponent() {
return (
<View>
{isWeb && <Text>Web platform</Text>}
{isAndroid && <Text>Android platform</Text>}
{isIos && <Text>iOS platform</Text>}
</View>
);
}
// Platform-specific styling
const PlatformView = styled(View, {
backgroundColor: '$background',
...isWeb && {
cursor: 'pointer',
userSelect: 'none',
},
...isAndroid && {
elevation: 4,
},
...isIos && {
shadowOpacity: 0.3,
shadowRadius: 4,
shadowOffset: { width: 0, height: 2 },
},
});interface AccessibilityAction {
name: string;
label?: string;
}
type AccessibilityRole =
| 'none'
| 'button'
| 'link'
| 'search'
| 'image'
| 'keyboardkey'
| 'text'
| 'adjustable'
| 'imagebutton'
| 'header'
| 'summary'
| 'alert'
| 'checkbox'
| 'combobox'
| 'menu'
| 'menubar'
| 'menuitem'
| 'progressbar'
| 'radio'
| 'radiogroup'
| 'scrollbar'
| 'spinbutton'
| 'switch'
| 'tab'
| 'tablist'
| 'timer'
| 'toolbar';
interface AccessibilityState {
disabled?: boolean;
selected?: boolean;
checked?: boolean | 'mixed';
busy?: boolean;
expanded?: boolean;
}
interface AccessibilityValue {
min?: number;
max?: number;
now?: number;
text?: string;
}
interface TextLayoutEvent {
nativeEvent: {
lines: TextLayoutLine[];
};
}
interface TextLayoutLine {
text: string;
x: number;
y: number;
width: number;
height: number;
descender: number;
capHeight: number;
baseline: number;
xHeight: number;
}Install with Tessl CLI
npx tessl i tessl/npm-tamagui--core