Skip to content
Development
Agent

ui-patterns

<!-- Loaded by react-native-engineer when task involves images, modals, Pressable, safe area, ScrollView, styling, galleries, menus, layout measurement -->

From plugin
vexjoy-agent
449198 skills198 agents12 commands83 hooks

Claude Code

$ npx -y skills add notque/vexjoy-agent --agent claude-code

Agent definition

ui-patterns.md

UI Patterns Reference

<!-- Loaded by react-native-engineer when task involves images, modals, Pressable, safe area, ScrollView, styling, galleries, menus, layout measurement -->

Use expo-image for All Image Rendering

**Impact:** HIGH — memory-efficient caching, blurhash placeholders, progressive loading, list recycling

**Instead of:**

import { Image } from 'react-native'
<Image source={{ uri: url }} style={styles.avatar} />

**Use:**

import { Image } from 'expo-image'

<Image
  source={{ uri: url }}
  placeholder={{ blurhash: 'LGF5]+Yk^6#M@-5c,1J5@[or[Q6.' }}
  contentFit="cover"
  transition={200}
  style={styles.avatar}
/>

Key props: `placeholder` (blurhash), `contentFit`, `transition`, `priority`, `cachePolicy`, `recyclingKey` (for lists). Cross-platform: `SolitoImage` from `solito/image`.

---

Use Native Modals Over JS-Based Bottom Sheets

**Impact:** HIGH — native gestures, accessibility, performance

**Instead of:** JS bottom sheet libraries.

**Use:**

<Modal
  visible={visible}
  presentationStyle="formSheet"
  animationType="slide"
  onRequestClose={() => setVisible(false)}
>
  <View style={{ padding: 16 }}><Text>Sheet content</Text></View>
</Modal>

With React Navigation v7:

<Stack.Screen
  name="Details"
  component={DetailsScreen}
  options={{ presentation: 'formSheet', sheetAllowedDetents: 'fitToContents' }}
/>

---

Use Pressable Instead of Touchable Components

**Impact:** LOW — modern API, more flexible

`TouchableOpacity`/`TouchableHighlight` are legacy. Use `Pressable` from `react-native`. In scrollable lists: `Pressable` from `react-native-gesture-handler`.

For animated press states, use `GestureDetector` with Reanimated — see `animation-patterns.md`.

---

Use contentInsetAdjustmentBehavior for Safe Areas

**Impact:** MEDIUM — native safe area handling, no layout shifts

**Instead of:**

<SafeAreaView style={{ flex: 1 }}><ScrollView>{children}</ScrollView></SafeAreaView>

**Use:**

<ScrollView contentInsetAdjustmentBehavior="automatic">{children}</ScrollView>

---

Use contentInset for Dynamic ScrollView Spacing

**Impact:** LOW — no layout recalculation on spacing changes

**Instead of:** `contentContainerStyle={{ paddingBottom: bottomOffset }}` (triggers layout recalc).

**Use:**

<ScrollView
  contentInset={{ bottom: bottomOffset }}
  scrollIndicatorInsets={{ bottom: bottomOffset }}
>
  {children}
</ScrollView>

Static spacing that never changes is fine as padding.

---

Modern Styling Patterns

**Impact:** MEDIUM

**Smooth corners**: `{ borderRadius: 12, borderCurve: 'continuous' }`

**Gap instead of margin**: `<View style={{ gap: 8 }}>`

**Linear gradients**: `{ experimental_backgroundImage: 'linear-gradient(to bottom, #000, #fff)' }`

**Box shadows**: `{ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)' }`

**Font hierarchy via weight/color, not size**: Use `fontWeight: '600'` for titles, `color: '#666'` for subtitles, `color: '#999'` for captions.

---

Use Galeria for Image Galleries with Lightbox

**Impact:** MEDIUM — native shared element transitions, pinch-to-zoom, pan-to-close

import { Galeria } from '@nandorojo/galeria'
import { Image } from 'expo-image'

<Galeria urls={urls}>
  {urls.map((url, index) => (
    <Galeria.Image index={index} key={url}>
      <Image source={{ uri: url }} style={styles.thumbnail} />
    </Galeria.Image>
  ))}
</Galeria>

---

Use Native Menus for Dropdowns and Context Menus

**Impact:** HIGH — native accessibility, platform-consistent UX

**Instead of:** Custom JS dropdown with absolute-positioned View.

**Use:**

import * as DropdownMenu from 'zeego/dropdown-menu'

<DropdownMenu.Root>
  <DropdownMenu.Trigger>
    <Pressable><Text>Options</Text></Pressable>
  </DropdownMenu.Trigger>
  <DropdownMenu.Content>
    <DropdownMenu.Item key="edit" onSelect={() => handleEdit()}>
      <DropdownMenu.ItemTitle>Edit</DropdownMenu.ItemTitle>
    </DropdownMenu.Item>
    <DropdownMenu.Item key="delete" destructive onSelect={() => handleDelete()}>
      <DropdownMenu.ItemTitle>Delete</DropdownMenu.ItemTitle>
    </DropdownMenu.Item>
  </DropdownMenu.Content>
</DropdownMenu.Root>

For long-press: `zeego/context-menu` with same API.

---

Measuring View Dimensions

**Impact:** MEDIUM — synchronous initial measurement, avoids unnecessary re-renders

const ref = useRef<View>(null)
const [size, setSize] = useState<Size | undefined>(undefined)

useLayoutEffect(() => {
  const rect = ref.current?.getBoundingClientRect()
  if (rect) setSize({ width: rect.width, height: rect.height })
}, [])

const onLayout = (e: LayoutChangeEvent) => {
  const { width, height } = e.nativeEvent.layout
  setSize((prev) => {
    if (prev?.width === width && prev?.height === height) return prev
    return { width, height }
  })
}

---

Use Compound Components for Flexible Button Composition

**Impact:** MEDIUM — explicit, composable API

**Instead of:** `<Button icon={<Icon />}>Save</Button>` (ambiguous).

**Use:**

<Button>
  <ButtonIcon><SaveIcon /></ButtonIcon>
  <ButtonText>Save</ButtonText>
</Button>
Read more
@notque
Ships withvexjoy-agent

Essays and writing behind this toolkit live at vexjoy.com. VexJoy Agent connects plain-English requests to specialist agents, skills, and workflows. /do selects the knowledge and tools needed for your task.

44964 skills198 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
449
Star history of notque/vexjoy-agent on GitHub, a chart by star-history.com

Other agents on vexjoy-agent.

modules

modules

Agent

**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes…

@notque@notqueInstall Agent
testing

testing

Agent

**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**:…

@notque@notqueInstall Agent
communication-patterns

communication-patterns

Agent

**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what…

@notque@notqueInstall Agent

More agents from other plugins.

a11y-architect
Auto-invoked

a11y-architect

Agent

Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI…

ecc13d293k
@affaan-m@affaan-mInstall Agent
analyze-pass-impact

analyze-pass-impact

Agent

Analyzes how a specific topic affects a group of compiler passes. Used by the /plan-update skill to parallelize research across…

react2d252k
@facebook@facebookInstall Agent
orchestrator

orchestrator

Agent

- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as…

codex2d135k
@openai@openaiInstall Agent
cavecrew-builder

cavecrew-builder

Agent

Surgical 1-2 file edit. Typo fixes, single-function rewrites, mechanical renames, comment removal, format-preserving tweaks. Hard…

caveman3d119k
@juliusbrussee@juliusbrusseeInstall Agent

Questions about ui-patterns.

Which coding agents can use ui-patterns?
ui-patterns installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.