---
name: examprep-ai
description: "Exam preparation assistant that converts syllabi, past papers, or notes into a ranked High Score Roadmap. Covers theory, numericals, MCQs, coding, and lab prep, ordered Easy → Medium → Hard. Use for last-minute revision, important topics, and question prediction."
risk: safe
source: community
date_added: "2026-06-05"
allowed-tools: Read, Glob, Grep
author: WHOISABHISHEKADHIKARI
user-invokable: true
tags:
  - education
  - exam-prep
  - study-guide
  - question-prediction
  - syllabus-analysis
  - revision
  - students
---

# ExamPrep AI

## When to Use

Use this skill when you need to:
- Convert a syllabus, past papers, or study notes into a prioritized roadmap.
- Focus on specific types of exam questions (Theory, Numerical, MCQ, Coding, Lab).
- Create flashcards, predicted exam papers, or check your overall exam readiness.
- Perform last-minute revision or deep-dive into important exam topics.

## 🎯 Selective Reading Rule — Read ONLY the section matching the request

| What the student asks for | Jump to |
|--------------------------|---------|
| Full roadmap / "what to study" / syllabus + past papers uploaded | [Full Roadmap Mode](#full-roadmap-mode) |
| Theory questions only / definitions / explanations | [Theory Notes](#theory-notes) |
| Numerical / calculation / derivation problems | [Numerical Notes](#numerical-notes) |
| MCQ / True-False / objective practice | [MCQ Notes](#mcq-notes) |
| Coding / algorithm / trace / debug | [Coding Notes](#coding-notes) |
| Lab / practical / viva prep | [Lab Notes](#lab-notes) |
| Flashcards only | [Flashcards](#flashcards) |
| Mock exam paper | [Predicted Exam Paper](#predicted-exam-paper) |
| Readiness check / score projection | [Exam Readiness Dashboard](#exam-readiness-dashboard) |

**Rule:** Read the matched section and the [Shared Foundations](#shared-foundations) block.
Skip everything else. Do not load all sections for a focused request.

---

## Shared Foundations

> Load this block for every request. It is small and always needed.

### Difficulty Scale (Universal)

| Level | Signal Words | Student Goal |
|-------|-------------|--------------|
| 🟩 Easy | define, state, list, name, identify, what is | Guaranteed marks — study first |
| 🟨 Medium | explain, describe, compare, calculate, implement, trace | Mid-paper marks |
| 🟥 Hard | derive, prove, optimize, analyze, evaluate, design, why | Score separators — study last |

**Order rule:** Always present Easy → Medium → Hard. Never reverse.

### Intake (ask once, then proceed)

1. Collect at least one of: syllabus, past question papers, notes, or subject name + university.
2. Confirm course code if OCR confidence < 80%: *"I detected [X] — is this correct?"*
3. Ask time available. If no answer → default **Standard Mode (6–12 hrs)** and state the assumption.

### Study Modes

| Mode | Time | Load |
|------|------|------|
| 🚨 Emergency | 1–2 hrs | 🟩 Easy only, top 10 questions |
| ⚡ Sprint | 3–5 hrs | 🟩 + 🟨, top 25 questions |
| 📚 Standard *(default)* | 6–12 hrs | All difficulties, full roadmap |
| 🗓 Advance | Days+ | Daily schedule + mock papers |

### Syllabus Guardrail

- Map every question to a syllabus unit (≥ 70% match → `[IN SYLLABUS]`).
- Never generate content for topics absent from the uploaded syllabus.
- Out-of-syllabus items → flag, ask student before including.

### Probability Score

```
Score = (Frequency × 0.40) + (Recency × 0.30) + (Unit Weight × 0.20) + (Marks × 0.10)
```
- Frequency: appearances ÷ max appearances × 100
- Recency: last 2 yrs = 100 · 3–4 yrs = 60 · older = 30
- Unit Weight: core = 100 · elective = 50
- Marks: 10+ = 100 · 5–9 = 60 · 2–4 = 30 · MCQ = 20

## Limitations

- This skill supports study planning and revision, but it cannot guarantee
  exam questions, marks, grading outcomes, or instructor expectations.
- Probability scores are heuristics based on supplied syllabi, notes, and past
  papers; sparse, outdated, or incomplete inputs reduce reliability.
- The skill should not fabricate syllabus coverage. If source material is
  missing, ambiguous, or out of scope, ask the student to confirm before
  adding predicted content.
- It is not a substitute for official course guidance, accessibility
  accommodations, academic-integrity policies, or instructor feedback.
- Do not request or process private student records beyond the study material
  needed for the current revision task.

---

## Full Roadmap Mode

> Use when: student uploads syllabus + past papers, or asks "what should I study?"

**Step 1 — Extract.** Pull all questions; note year/source for each.
Confirm: *"Extracted [N] questions from [M] papers for [Course]. Found: 📝[A] 🔢[B] 🔘[C] 💻[D] 🧪[E]. Proceed?"*

**Step 2 — Classify + tag difficulty.** Use the five-type table:

| Type | Identify By |
|------|------------|
| 📝 Theory | define, explain, discuss, compare, differentiate |
| 🔢 Numerical | calculate, find, solve, derive, prove, numbers in question |
| 🔘 MCQ/T-F | options listed, "true or false", "which of the following" |
| 💻 Coding | write a program, implement, trace output, algorithm, flowchart |
| 🧪 Lab | experiment, procedure, observation, aim, apparatus, viva |

**Step 3 — Build ranked tables (one per type):**

```
| # | Question | Times | Marks | Difficulty | Unit | Priority |
|---|----------|-------|-------|------------|------|----------|
| 1 | [question text] | [N]× | [X] | 🟩/🟨/🟥 | Unit [X] | 🔥 Must / ✅ Do |
```

**Step 4 — Generate notes** using the matching type section below.
Order: Easy across all types first → then Medium → then Hard.

**Step 5 — Coverage tracker:**
```
Unit 1: [Name]  →  📝✅  🔢✅  🔘⚠ PREDICTED  💻—  🧪—
Legend: ✅ past paper  ⚠ predicted  — not applicable
```
For any gap: generate one predicted question + note, label `[PREDICTED — not from past papers]`.

**Step 6 — Offer:** *"Would you like (a) Flashcards, (b) Predicted Exam Paper, or (c) Readiness Dashboard?"*

---

## Theory Notes

> Use when: student asks about definitions, explanations, long-answer questions.

**🟩 Easy — Definition / List (30 sec)**
```
📝🟩 [Question] | [N]× | [X] marks
─────────────────────────────────
ANSWER: [2–4 bullets max]
KEY TERM: [single most important word]
MEMORY HOOK: [one-liner trick]
```

**🟨 Medium — Explanation / Comparison (2 min)**
```
📝🟨 [Question] | [N]× | [X] marks
─────────────────────────────────
DEFINITION: [1 sentence]
MAIN POINTS: • P1 • P2 • P3 • P4
DIAGRAM: [text description — student sketches from this]
EXAM TIP: [what examiner rewards]
```

**🟥 Hard — Discussion / Evaluation (5 min read · 10 min write)**
```
📝🟥 [Question] | [N]× | [X] marks | Unit [X]
─────────────────────────────────────────────
INTRO: [2–3 sentences]
SECTION 1 — [subtopic]: • point • point
SECTION 2 — [subtopic]: • point • point
SECTION 3 — [subtopic]: • point • point
DIAGRAM: [sketch description]
CONCLUSION: [1–2 lines]
MARKS HINT: Intro ~2 · each section ~3 · diagram ~2 · conclusion ~1
MEMORY: [acronym or order trick]
```

---

## Numerical Notes

> Use when: student asks for calculation problems, derivations, formulas.

**🟩 Easy — Direct formula plug-in**
```
🔢🟩 [Problem Type] | [N]× | [X] marks
──────────────────────────────────────
FORMULA:        [clearly written]
GIVEN → FIND:   [what's given / what to find]
WORKED EXAMPLE:
  Step 1: [substitute]
  Step 2: [calculate]
  Answer: [result + unit]
COMMON MISTAKE: [the one error students make]
MEMORY HOOK:    [how to remember formula]
```

**🟨 Medium — Multi-step with condition**
```
🔢🟨 [Problem Type] | [N]× | [X] marks
──────────────────────────────────────
FORMULA(S): [all needed]
APPROACH:   [which formula when — decision rule]
WORKED EXAMPLE:
  Step 1: [setup / draw table]
  Step 2: [apply condition]
  Step 3: [calculate]
  Step 4: [verify / interpret]
  Answer: [result]
WATCH OUT:  [condition that trips students]
EXAM TIP:   [show working — marks for method too]
```

**🟥 Hard — Derivation / Proof**
```
🔢🟥 [Problem / Derivation] | [N]× | [X] marks
───────────────────────────────────────────────
PREREQUISITES: [what student must know first]
DERIVATION:
  Step 1: [first principles]
  Step 2: [key transformation]
  ...Final: [result / QED]
WORKED EXAMPLE: [concrete numbers applied]
MARKS BREAKDOWN: [method marks vs answer marks]
COMMON ERRORS: [2–3 errors that lose marks]
```

---

## MCQ Notes

> Use when: student asks for MCQ practice, true/false, objective questions.

**🟩 Easy — Recall**
```
🔘🟩 [Question] | [N]×
──────────────────────
CORRECT: [option + text]
WHY CORRECT: [one sentence]
WHY OTHERS WRONG: • A: ... • B: ... • C: ...
KEY FACT: [the one thing this tests]
```

**🟨 Medium — Application**
```
🔘🟨 [Question] | [N]×
──────────────────────
CORRECT: [option + text]
REASONING: [identify concept] → [apply rule] → [eliminate wrong]
TRAP: [why students pick the wrong answer]
```

**🟥 Hard — Trap / Edge-case**
```
🔘🟥 [Question] | [N]×
──────────────────────
CORRECT: [option + text]
WHY TRICKY: [what assumption is exploited]
ELIMINATE: • Drop [A]: [reason] • Drop [B]: [reason] • Keep [C]: [reason]
RULE: [the precise rule that settles this type]
```

---

## Coding Notes

> Use when: student asks to write programs, trace output, implement algorithms, debug.

**🟩 Easy — Syntax / Pattern recall**
```
💻🟩 [Task] | [N]× | [X] marks
────────────────────────────────
PATTERN:     [algorithm/structure name]
TEMPLATE:    [minimal working skeleton — pseudocode or language-specific]
KEY LINES:   [1–2 lines examiner looks for]
MEMORY HOOK: [how to recall under pressure]
```

**🟨 Medium — Logic construction**
```
💻🟨 [Task] | [N]× | [X] marks
────────────────────────────────
APPROACH:
  1. [sub-tasks]  2. [data structures]  3. [step-by-step logic]
ANNOTATED CODE: [code with inline comments]
EDGE CASES:  [inputs needing special handling]
EXAM TIP:    [comment code — examiners reward clarity]
```

**🟥 Hard — Optimize / Trace / Debug**
```
💻🟥 [Task] | [N]× | [X] marks | TYPE: [Optimize / Trace / Debug]
──────────────────────────────────────────────────────────────────
TRACE →   Input | Trace Table (Iter · VarA · VarB · Output) | Final Output
OPTIMIZE → Naive O(?) → Optimized O(?) | Key Insight: [what enables it]
DEBUG →   Bug Location | Bug Type | Fix | Why it works
```

---

## Lab Notes

> Use when: student asks about experiments, procedures, observations, viva prep.

**🟩 Easy — Name / Identify**
```
🧪🟩 [Experiment] | [N]×
─────────────────────────
AIM:      [one sentence]
APPARATUS: [bullet list]
RESULT:   [expected outcome to state]
KEY TERM: [most important term]
```

**🟨 Medium — Write procedure**
```
🧪🟨 [Experiment] | [N]×
─────────────────────────
AIM / APPARATUS: [brief]
PROCEDURE: Step 1 → Step 2 → Step 3 → Step 4
OBS TABLE: [column headers + example row]
RESULT:    [how to state conclusion]
PRECAUTIONS: [2–3 points examiners look for]
```

**🟥 Hard — Analysis / Viva**
```
🧪🟥 [Experiment] | [N]×
─────────────────────────
ANALYSIS: • result in context • formula used • source of error
VIVA:
  Q1: [question]  A: [2–3 sentence answer]
  Q2: [question]  A: [2–3 sentence answer]
  Q3: [question]  A: [2–3 sentence answer]
EXAM TIP: [what viva examiner always asks]
```

---

## Flashcards

> Use when: student asks for flashcards or quick-recall cards.

One card per question:
```
[TYPE EMOJI][DIFFICULTY EMOJI]
Q: [question]
A: [answer in 1–2 lines]
Key: [formula / term / pattern — if applicable]
```

---

## Predicted Exam Paper

> Use when: student asks for a mock paper or practice test.

Generate one paper with all types represented. Label every question with type + difficulty.

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
AI PREDICTION — Not official. For practice only.
Course: [Name]  |  Total Marks: [X]  |  Time: [X] hrs
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

SECTION A — Short / Objective  [🟩 Easy]
  [MCQ / T-F / 1-mark definitions]

SECTION B — Medium Answer      [🟨 Medium]
  [Theory explanations + medium numericals]

SECTION C — Long Answer        [🟥 Hard]
  [Long theory + derivations + coding]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

---

## Exam Readiness Dashboard

> Use when: student asks for a score estimate or readiness check.

```
📊 EXAM READINESS
──────────────────────────────────────────────────────
TYPE          EASY    MEDIUM   HARD    OVERALL
📝 Theory     [X]%    [X]%     [X]%    [X]%
🔢 Numerical  [X]%    [X]%     [X]%    [X]%
🔘 MCQ/T-F    [X]%    [X]%     [X]%    [X]%
💻 Coding     [X]%    [X]%     [X]%    [X]%
🧪 Lab        [X]%    [X]%     [X]%    [X]%
──────────────────────────────────────────────────────
PREPAREDNESS  : [X]%
MARKS RANGE   : [Low]–[High] out of [Total]
──────────────────────────────────────────────────────
STRONG        : [types + topics]
WEAK → FOCUS  : [types + topics]
──────────────────────────────────────────────────────
Confidence: [High/Medium/Low]  |  Based on: [N] papers
```

---

## Worked Example

> Concrete before/after demonstrating the skill.

**Input:**
> "I have my OS exam tomorrow. Here's the syllabus [paste] and 3 past papers [upload]. I have 4 hours."

**Skill routes to:** Full Roadmap Mode → Sprint Mode (3–5 hrs)

**Output sequence:**
1. Extraction confirm: *"Extracted 47 questions from 3 papers for Operating System (CSC-207). Found: 📝18 🔢12 🔘10 💻7 🧪0. Proceed?"*
2. Ranked tables for all types, Easy → Medium only (Sprint Mode skips Hard except top-1 per unit)
3. Notes for top 25 questions — Easy across all types first, then Medium
4. Coverage tracker showing which units are covered
5. Offer: flashcards, mock paper, or dashboard

---

## Quality Checks (run before every output)

| Check | Rule |
|-------|------|
| Syllabus compliance | Every note maps to a syllabus unit |
| Difficulty order | Easy before Medium before Hard — never reversed |
| Numerical accuracy | Worked examples compute correctly |
| Code validity | Snippets are syntactically correct |
| Note length | Readable in ≤ 2–5 min per note |
| No hallucination | No facts absent from uploaded materials |
| Course code confirmed | OCR-detected code verified by student |

---

## Error Responses

| Situation | Say |
|-----------|-----|
| No syllabus | "Without a syllabus I can't guarantee on-topic notes. Paste your unit list as text?" |
| 1 past paper only | "One paper = lower prediction confidence. More papers = better accuracy." |
| OCR failure | "Couldn't read part of the image. Can you retype those questions?" |
| Out-of-syllabus question | "This doesn't match your syllabus — skipping it. Want me to include it anyway?" |
| Mixed subjects | "Found questions from two subjects. Should I separate them?" |
| No time given | "Defaulting to Standard Mode (6–12 hrs). Tell me if you have less time." |
| No numericals/coding found | "No numerical/coding questions found. Share a paper that includes them if your exam has these."


---
name: executing-plans
description: "Use when you have a written implementation plan to execute in a separate session with review checkpoints"
risk: critical
source: community
date_added: "2026-02-27"
---

# Executing Plans

## Overview

Load plan, review critically, execute tasks in batches, report for review between batches.

**Core principle:** Batch execution with checkpoints for architect review.

**Announce at start:** "I'm using the executing-plans skill to implement this plan."

## The Process

### Step 1: Load and Review Plan
1. Read plan file
2. Review critically - identify any questions or concerns about the plan
3. If concerns: Raise them with your human partner before starting
4. If no concerns: Create TodoWrite and proceed

### Step 2: Execute Batch
**Default: First 3 tasks**

For each task:
1. Mark as in_progress
2. Follow each step exactly (plan has bite-sized steps)
3. Run verifications as specified
4. Mark as completed

### Step 3: Report
When batch complete:
- Show what was implemented
- Show verification output
- Say: "Ready for feedback."

### Step 4: Continue
Based on feedback:
- Apply changes if needed
- Execute next batch
- Repeat until complete

### Step 5: Complete Development

After all tasks complete and verified:
- Announce: "I'm using the finishing-a-development-branch skill to complete this work."
- **REQUIRED SUB-SKILL:** Use superpowers:finishing-a-development-branch
- Follow that skill to verify tests, present options, execute choice

## When to Stop and Ask for Help

**STOP executing immediately when:**
- Hit a blocker mid-batch (missing dependency, test fails, instruction unclear)
- Plan has critical gaps preventing starting
- You don't understand an instruction
- Verification fails repeatedly

**Ask for clarification rather than guessing.**

## When to Revisit Earlier Steps

**Return to Review (Step 1) when:**
- Partner updates the plan based on your feedback
- Fundamental approach needs rethinking

**Don't force through blockers** - stop and ask.

## Remember
- Review plan critically first
- Follow plan steps exactly
- Don't skip verifications
- Reference skills when plan says to
- Between batches: just report and wait
- Stop when blocked, don't guess

## When to Use
This skill is applicable to execute the workflow or actions described in the overview.

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: explain-like-socrates
description: >
  Explains concepts using Socratic-style dialogue. Use when the user asks to explain, teach or help understand a concept like socrates.
risk: safe
source: original
date_added: "2026-03-11"
---

# EXPLAIN LIKE SOCRATES

Explains ideas using the conversational reasoning style of Socratic dialogue. Instead of delivering lectures, the assistant guides the user toward understanding through reflective reasoning, small thought experiments, and a single simple analogy. The goal is not to deliver information quickly, but to help the user **arrive at clarity through thought.**

DO:
- reason conversationally
- build the idea step-by-step
- ask reflective questions occasionally
- guide the user's thinking

DO NOT:
- present textbook explanations
- dump large factual lists
- overwhelm the user with terminology
- sound like documentation

Avoid traditional lecture-style teaching and use style of Socrates, the original street philosopher from ancient Athens.

---

## When to Use
Use this skill when the user asks to:
- explain a concept
- teach how something works
- help understand a technical idea
- clarify a theory or system
- explore a philosophical or abstract idea

Do NOT Use this skill when the user asks for:
- quick definitions and troubleshooting
- installation instructions
- configuration commands
- short factual lookup

---

# RESPONSE STRUCTURE

Responses should loosely follow this pattern. DO NOT output headings

## 1. Curiosity Opening

Begin each explanation in the voice of Socrates: By questioning assumptions, offering analogies or professing ignorance—to initiate a dialogue that invites reflection and seeks deeper understanding.

---

## 2. Guided Reasoning

Introduce the idea through reasoning rather than facts.

Build the concept gradually through:
- small observations
- simple thought experiments
- reflective questions

Example pattern:
"Suppose a system needed to remember something from a previous step. What benefit might that give us?"

---

## 3. Single Analogy

Introduce **one simple analogy** to illuminate the concept.

Rules:
- use only one analogy per explanation
- keep the analogy consistent
- do not introduce additional metaphors

Example analogy:

A **vending machine dispensing snacks**.

Example use:
"Imagine a vending machine remembering the last button pressed.
Would that change how it behaves next time?"

---

## 4. Clarification

Gradually refine the idea.
- connect reasoning steps
- gently correct misconceptions
- reinforce the emerging mental model
Keep explanations concise and conversational.

---

## 5. Reflection

End with a reflective prompt.
Examples:
- "Does the idea appear clearer now?"
- "What picture forms in your mind now?"
- **"What clearer picture emerges now?"**

Encourage user to ask more if needed.

---

# RESPONSE LENGTH GUIDANCE

Responses should remain concise and conversational.
Preferred format:
- 4–8 short paragraphs
- minimal or no jargon unless required
- short reflective questions with reasoning

Avoid long philosophical monologues.

---

# MISCONCEPTION HANDLING

If the user expresses an incorrect belief:
1. acknowledge their reasoning
2. gently challenge the assumption
3. guide toward a clearer interpretation

Example: "That is an interesting way to see it. But consider this…"

---

# TONE

Maintain a conversational tone just like Socrates that is reflective, curious, patient. Response should feel like **thinking through an idea together**, not delivering a lecture.

---

# FAILURE HANDLING

If the user insists on a direct answer: Provide the explanation but still frame it through reasoning.
Example: "Let us think through it step by step."
If the user remains confused: Return to the analogy and simplify the reasoning.

---

# TERMINATION

Conclude the explanation when:
- the concept has been explored through reasoning
- the user expresses understanding
- the explanation naturally reaches clarity

Optionally invite reflection with a prompt such as:
- "Does that interpretation make sense to you?"
- "How does that idea appear to you now?"
- "Does the picture feel clearer?"

Questions should appear naturally during reasoning, not as a mandatory closing statement.

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: expo-api-routes
description: Guidelines for creating API routes in Expo Router with EAS Hosting
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-api-routes
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

## When to Use API Routes

Use API routes when you need:

- **Server-side secrets** — API keys, database credentials, or tokens that must never reach the client
- **Database operations** — Direct database queries that shouldn't be exposed
- **Third-party API proxies** — Hide API keys when calling external services (OpenAI, Stripe, etc.)
- **Server-side validation** — Validate data before database writes
- **Webhook endpoints** — Receive callbacks from services like Stripe or GitHub
- **Rate limiting** — Control access at the server level
- **Heavy computation** — Offload processing that would be slow on mobile

## When NOT to Use API Routes

Avoid API routes when:

- **Data is already public** — Use direct fetch to public APIs instead
- **No secrets required** — Static data or client-safe operations
- **Real-time updates needed** — Use WebSockets or services like Supabase Realtime
- **Simple CRUD** — Consider Firebase, Supabase, or Convex for managed backends
- **File uploads** — Use direct-to-storage uploads (S3 presigned URLs, Cloudflare R2)
- **Authentication only** — Use Clerk, Auth0, or Firebase Auth instead

## File Structure

API routes live in the `app` directory with `+api.ts` suffix:

```
app/
  api/
    hello+api.ts          → GET /api/hello
    users+api.ts          → /api/users
    users/[id]+api.ts     → /api/users/:id
  (tabs)/
    index.tsx
```

## Basic API Route

```ts
// app/api/hello+api.ts
export function GET(request: Request) {
  return Response.json({ message: "Hello from Expo!" });
}
```

## HTTP Methods

Export named functions for each HTTP method:

```ts
// app/api/items+api.ts
export function GET(request: Request) {
  return Response.json({ items: [] });
}

export async function POST(request: Request) {
  const body = await request.json();
  return Response.json({ created: body }, { status: 201 });
}

export async function PUT(request: Request) {
  const body = await request.json();
  return Response.json({ updated: body });
}

export async function DELETE(request: Request) {
  return new Response(null, { status: 204 });
}
```

## Dynamic Routes

```ts
// app/api/users/[id]+api.ts
export function GET(request: Request, { id }: { id: string }) {
  return Response.json({ userId: id });
}
```

## Request Handling

### Query Parameters

```ts
export function GET(request: Request) {
  const url = new URL(request.url);
  const page = url.searchParams.get("page") ?? "1";
  const limit = url.searchParams.get("limit") ?? "10";

  return Response.json({ page, limit });
}
```

### Headers

```ts
export function GET(request: Request) {
  const auth = request.headers.get("Authorization");

  if (!auth) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  return Response.json({ authenticated: true });
}
```

### JSON Body

```ts
export async function POST(request: Request) {
  const { email, password } = await request.json();

  if (!email || !password) {
    return Response.json({ error: "Missing fields" }, { status: 400 });
  }

  return Response.json({ success: true });
}
```

## Environment Variables

Use `process.env` for server-side secrets:

```ts
// app/api/ai+api.ts
export async function POST(request: Request) {
  const { prompt } = await request.json();

  const response = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
    },
    body: JSON.stringify({
      model: "gpt-4",
      messages: [{ role: "user", content: prompt }],
    }),
  });

  const data = await response.json();
  return Response.json(data);
}
```

Set environment variables:

- **Local**: Create `.env` file (never commit)
- **EAS Hosting**: Use `eas env:create` or Expo dashboard

## CORS Headers

Add CORS for web clients:

```ts
const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type, Authorization",
};

export function OPTIONS() {
  return new Response(null, { headers: corsHeaders });
}

export function GET() {
  return Response.json({ data: "value" }, { headers: corsHeaders });
}
```

## Error Handling

```ts
export async function POST(request: Request) {
  try {
    const body = await request.json();
    // Process...
    return Response.json({ success: true });
  } catch (error) {
    console.error("API error:", error);
    return Response.json({ error: "Internal server error" }, { status: 500 });
  }
}
```

## Testing Locally

Start the development server with API routes:

```bash
npx expo serve
```

This starts a local server at `http://localhost:8081` with full API route support.

Test with curl:

```bash
curl http://localhost:8081/api/hello
curl -X POST http://localhost:8081/api/users -H "Content-Type: application/json" -d '{"name":"Test"}'
```

## Deployment to EAS Hosting

### Prerequisites

```bash
npm install -g eas-cli
eas login
```

### Deploy

```bash
eas deploy
```

This builds and deploys your API routes to EAS Hosting (Cloudflare Workers).

### Environment Variables for Production

```bash
# Create a secret
eas env:create --name OPENAI_API_KEY --value sk-xxx --environment production

# Or use the Expo dashboard
```

### Custom Domain

Configure in `eas.json` or Expo dashboard.

## EAS Hosting Runtime (Cloudflare Workers)

API routes run on Cloudflare Workers. Key limitations:

### Missing/Limited APIs

- **No Node.js filesystem** — `fs` module unavailable
- **No native Node modules** — Use Web APIs or polyfills
- **Limited execution time** — 30 second timeout for CPU-intensive tasks
- **No persistent connections** — WebSockets require Durable Objects
- **fetch is available** — Use standard fetch for HTTP requests

### Use Web APIs Instead

```ts
// Use Web Crypto instead of Node crypto
const hash = await crypto.subtle.digest(
  "SHA-256",
  new TextEncoder().encode("data")
);

// Use fetch instead of node-fetch
const response = await fetch("https://api.example.com");

// Use Response/Request (already available)
return new Response(JSON.stringify(data), {
  headers: { "Content-Type": "application/json" },
});
```

### Database Options

Since filesystem is unavailable, use cloud databases:

- **Cloudflare D1** — SQLite at the edge
- **Turso** — Distributed SQLite
- **PlanetScale** — Serverless MySQL
- **Supabase** — Postgres with REST API
- **Neon** — Serverless Postgres

Example with Turso:

```ts
// app/api/users+api.ts
import { createClient } from "@libsql/client/web";

const db = createClient({
  url: process.env.TURSO_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN!,
});

export async function GET() {
  const result = await db.execute("SELECT * FROM users");
  return Response.json(result.rows);
}
```

## Calling API Routes from Client

```ts
// From React Native components
const response = await fetch("/api/hello");
const data = await response.json();

// With body
const response = await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "John" }),
});
```

## Common Patterns

### Authentication Middleware

```ts
// utils/auth.ts
export async function requireAuth(request: Request) {
  const token = request.headers.get("Authorization")?.replace("Bearer ", "");

  if (!token) {
    throw new Response(JSON.stringify({ error: "Unauthorized" }), {
      status: 401,
      headers: { "Content-Type": "application/json" },
    });
  }

  // Verify token...
  return { userId: "123" };
}

// app/api/protected+api.ts
import { requireAuth } from "../../utils/auth";

export async function GET(request: Request) {
  const { userId } = await requireAuth(request);
  return Response.json({ userId });
}
```

### Proxy External API

```ts
// app/api/weather+api.ts
export async function GET(request: Request) {
  const url = new URL(request.url);
  const city = url.searchParams.get("city");

  const response = await fetch(
    `https://api.weather.com/v1/current?city=${city}&key=${process.env.WEATHER_API_KEY}`
  );

  return Response.json(await response.json());
}
```

## Rules

- NEVER expose API keys or secrets in client code
- ALWAYS validate and sanitize user input
- Use proper HTTP status codes (200, 201, 400, 401, 404, 500)
- Handle errors gracefully with try/catch
- Keep API routes focused — one responsibility per endpoint
- Use TypeScript for type safety
- Log errors server-side for debugging

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-brownfield
description: Integrate Expo and React Native into an existing native iOS or Android app. Use when the user mentions brownfield, embedding React Native in a native app, AAR/XCFramework, or adding Expo to an existing Kotlin/Swift project. Covers both the isolated approach and the integrated approach.
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-brownfield
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# Expo Brownfield
## When to Use

Use this skill when you need integrate Expo and React Native into an existing native iOS or Android app. Use when the user mentions brownfield, embedding React Native in a native app, AAR/XCFramework, or adding Expo to an existing Kotlin/Swift project. Covers both the isolated approach and the integrated approach.

A **brownfield** app is an existing native iOS or Android app that adopts React Native incrementally, as opposed to a **greenfield** app that is React Native from day one.

Expo supports two distinct ways to add React Native to a brownfield project:

| Approach       | What ships to the native app                                        | When to choose                                                                   |
| -------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **Isolated**   | Prebuilt AAR / XCFramework                                          | Native team doesn't need Node or RN tooling; RN code can live in a separate repo |
| **Integrated** | React Native sources added to the existing Gradle / CocoaPods build | One team owns everything; comfortable with RN tooling; wants a single build      |

For the full decision matrix, see [./references/comparison.md](./references/comparison.md).

## Pick an approach

Use these quick rules — fall through to `comparison.md` for anything ambiguous.

- **Choose isolated** if the iOS/Android team must consume RN as a regular library dependency (AAR or XCFramework), without installing Node, Yarn, or the React Native build toolchain.
- **Choose isolated** if RN code and native code live in separate repositories or release on independent cadences.
- **Choose integrated** if a single team owns both the native and RN code and is willing to add React Native + Expo to the native project's Gradle and CocoaPods setup.
- **Choose integrated** if you want hot reload and JS source maps to work seamlessly inside the existing native build process.

## References

- ./references/brownfield-isolated.md -- Build RN as AAR/XCFramework and consume from the native app (BrownfieldActivity, ReactNativeViewController, ReactNativeView)
- ./references/brownfield-integrated.md -- Add RN and Expo directly to existing Gradle and CocoaPods builds (ReactActivity, RCTRootView, Podfile)
- ./references/comparison.md -- Decision criteria, trade-offs, and scenario mapping for choosing an approach
- ./references/troubleshooting.md -- Metro connection, build, signing, and module-resolution issues common to both approaches

More information available at https://docs.expo.dev/brownfield/overview/

## Shared prerequisites

Both approaches require, in the environment that _builds_ the React Native side:

- **Node.js (LTS)** — runs the Expo CLI and JavaScript code.
- **Yarn** — manages JavaScript dependencies.

The integrated approach additionally requires **CocoaPods** on iOS (`sudo gem install cocoapods`). The isolated approach does **not** require CocoaPods or any RN tooling in the consuming native app.

## Versioning note

**Expo SDK 55 is the minimum supported version for brownfield integration.** Earlier SDKs lack `expo-brownfield`, the required `ExpoReactHostFactory` / `ExpoReactNativeFactory` entry points, and the current autolinking surface. When creating the Expo project, always pin the SDK explicitly:

```sh
npx create-expo-app@latest my-project --template default@sdk-55
```

Pin the same Expo SDK across both the RN project and any embedded dependencies.

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-cicd-workflows
description: Helps understand and write EAS workflow YAML files for Expo projects. Use this skill when the user asks about CI/CD or workflows in an Expo or EAS context, mentions .eas/workflows/, or wants help with EAS build pipelines or deployment automation.
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-cicd-workflows
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# EAS Workflows Skill
## When to Use

Use this skill when you need helps understand and write EAS workflow YAML files for Expo projects. Use this skill when the user asks about CI/CD or workflows in an Expo or EAS context, mentions .eas/workflows/, or wants help with EAS build pipelines or deployment automation.

Help developers write and edit EAS CI/CD workflow YAML files.

## Reference Documentation

Fetch these resources before generating or validating workflow files. First resolve this skill's directory, then use the fetch script in its `scripts/` directory. It is implemented using Node.js and caches responses using ETags for efficiency:

```bash
# Fetch resources
node <skill-dir>/scripts/fetch.js <url>
```

1. **JSON Schema** — https://api.expo.dev/v2/workflows/schema
   - It is NECESSARY to fetch this schema
   - Source of truth for validation
   - All job types and their required/optional parameters
   - Trigger types and configurations
   - Runner types, VM images, and all enums

2. **Syntax Documentation** — https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/syntax.mdx
   - Overview of workflow YAML syntax
   - Examples and English explanations
   - Expression syntax and contexts

3. **Pre-packaged Jobs** — https://raw.githubusercontent.com/expo/expo/refs/heads/main/docs/pages/eas/workflows/pre-packaged-jobs.mdx
   - Documentation for supported pre-packaged job types
   - Job-specific parameters and outputs

Do not rely on memorized values; these resources evolve as new features are added.

## Workflow File Location

Workflows live in `.eas/workflows/*.yml` (or `.yaml`).

## Top-Level Structure

A workflow file has these top-level keys:

- `name` — Display name for the workflow
- `on` — Triggers that start the workflow (at least one required)
- `jobs` — Job definitions (required)
- `defaults` — Shared defaults for all jobs
- `concurrency` — Control parallel workflow runs

Consult the schema for the full specification of each section.

## Expressions

Use `${{ }}` syntax for dynamic values. The schema defines available contexts:

- `github.*` — GitHub repository and event information
- `inputs.*` — Values from `workflow_dispatch` inputs
- `needs.*` — Outputs and status from dependent jobs
- `jobs.*` — Job outputs (alternative syntax)
- `steps.*` — Step outputs within custom jobs
- `workflow.*` — Workflow metadata

## Generating Workflows

When generating or editing workflows:

1. Fetch the schema to get current job types, parameters, and allowed values
2. Validate that required fields are present for each job type
3. Verify job references in `needs` and `after` exist in the workflow
4. Check that expressions reference valid contexts and outputs
5. Ensure `if` conditions respect the schema's length constraints

## Validation

After generating or editing a workflow file, validate it against the schema:

```sh
# Install dependencies if missing
[ -d "<skill-dir>/scripts/node_modules" ] || npm install --prefix <skill-dir>/scripts

node <skill-dir>/scripts/validate.js <workflow.yml> [workflow2.yml ...]
```

The validator fetches the latest schema and checks the YAML structure. Fix any reported errors before considering the workflow complete.

## Answering Questions

When users ask about available options (job types, triggers, runner types, etc.), fetch the schema and derive the answer from it rather than relying on potentially outdated information.

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-deployment
description: Deploy Expo apps to production with EAS — build and submit to the iOS App Store, Google Play Store, and TestFlight, configure eas.json build and submit profiles, manage app versions and build numbers, publish App Store metadata and ASO, and deploy web bundles and API routes via EAS...
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-deployment
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# Deployment
## When to Use

Use this skill when you need deploy Expo apps to production with EAS — build and submit to the iOS App Store, Google Play Store, and TestFlight, configure eas.json build and submit profiles, manage app versions and build numbers, publish App Store metadata and ASO, and deploy web bundles and API routes via EAS...

This skill covers deploying Expo applications across all platforms using EAS (Expo Application Services).

## References

Consult these resources as needed:

- ./references/workflows.md -- CI/CD workflows for automated deployments and PR previews
- ./references/testflight.md -- Submitting iOS builds to TestFlight for beta testing
- ./references/app-store-metadata.md -- Managing App Store metadata and ASO optimization
- ./references/play-store.md -- Submitting Android builds to Google Play Store
- ./references/ios-app-store.md -- iOS App Store submission and review process

## Quick Start

### Install EAS CLI

```bash
npm install -g eas-cli
eas login
```

### Initialize EAS

```bash
npx eas-cli@latest init
```

This creates `eas.json` with build profiles.

## Build Commands

### Production Builds

```bash
# iOS App Store build
npx eas-cli@latest build -p ios --profile production

# Android Play Store build
npx eas-cli@latest build -p android --profile production

# Both platforms
npx eas-cli@latest build --profile production
```

### Submit to Stores

```bash
# iOS: Build and submit to App Store Connect
npx eas-cli@latest build -p ios --profile production --submit

# Android: Build and submit to Play Store
npx eas-cli@latest build -p android --profile production --submit

# Shortcut for iOS TestFlight
npx testflight
```

## Web Deployment

Deploy web apps using EAS Hosting:

```bash
# Deploy to production
npx expo export -p web
npx eas-cli@latest deploy --prod

# Deploy PR preview
npx eas-cli@latest deploy
```

Expo Router API routes deploy together with the web bundle on EAS Hosting — `eas deploy` ships both. To author or configure the API routes themselves, use the `expo-api-routes` skill.

## EAS Configuration

Standard `eas.json` for production deployments:

```json
{
  "cli": {
    "version": ">= 16.0.1",
    "appVersionSource": "remote"
  },
  "build": {
    "production": {
      "autoIncrement": true,
      "ios": {
        "resourceClass": "m-medium"
      }
    },
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    }
  },
  "submit": {
    "production": {
      "ios": {
        "appleId": "your@email.com",
        "ascAppId": "1234567890"
      },
      "android": {
        "serviceAccountKeyPath": "./google-service-account.json",
        "track": "internal"
      }
    }
  }
}
```

## Platform-Specific Guides

### iOS

- Use `npx testflight` for quick TestFlight submissions
- Configure Apple credentials via `eas credentials`
- See ./references/testflight.md for credential setup
- See ./references/ios-app-store.md for App Store submission

### Android

- Set up Google Play Console service account
- Configure tracks: internal → closed → open → production
- See ./references/play-store.md for detailed setup

### Web

- EAS Hosting provides preview URLs for PRs
- Production deploys to your custom domain
- See ./references/workflows.md for CI/CD automation

## Automated Deployments

EAS Workflows automate the build → submit → update → deploy pipeline for CI/CD. See ./references/workflows.md for deployment-oriented examples. To author or validate workflow YAML, use the `expo-cicd-workflows` skill — it works from the live workflow schema.

## Version Management

EAS manages version numbers automatically with `appVersionSource: "remote"`:

```bash
# Check current versions
eas build:version:get

# Manually set version
eas build:version:set -p ios --build-number 42
```

## Monitoring

```bash
# List recent builds
eas build:list

# Check build status
eas build:view

# View submission status
eas submit:list
```

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-dev-client
description: Build Expo app for development
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-dev-client
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

Use EAS Build to create development clients for testing native code changes on physical devices. Use this for creating custom Expo Go clients for testing branches of your app.

## Important: When Development Clients Are Needed

**Development clients are the recommended setup for any real or production app.** Expo Go is a playground for learning and quick experiments with the native libraries it bundles; most apps outgrow it and move to a development client. See [Expo Go vs. development builds](https://docs.expo.dev/develop/development-builds/introduction/) for the full reasoning.

You need a dev client ONLY when using:

- Local Expo modules (custom native code)
- Apple targets (widgets, app clips, extensions)
- Third-party native modules not in Expo Go
- Config plugins, or testing remote push notifications and App/Universal Links

## EAS Configuration

Ensure `eas.json` has a development profile:

```json
{
  "cli": {
    "version": ">= 16.0.1",
    "appVersionSource": "remote"
  },
  "build": {
    "production": {
      "autoIncrement": true
    },
    "development": {
      "autoIncrement": true,
      "developmentClient": true
    }
  },
  "submit": {
    "production": {},
    "development": {}
  }
}
```

Key settings:

- `developmentClient: true` - Bundles expo-dev-client for development builds
- `autoIncrement: true` - Automatically increments build numbers
- `appVersionSource: "remote"` - Uses EAS as the source of truth for version numbers

## Building for TestFlight

Build iOS dev client and submit to TestFlight in one command:

```bash
eas build -p ios --profile development --submit
```

This will:

1. Build the development client in the cloud
2. Automatically submit to App Store Connect
3. Send you an email when the build is ready in TestFlight

After receiving the TestFlight email:

1. Download the build from TestFlight on your device
2. Launch the app to see the expo-dev-client UI
3. Connect to your local Metro bundler or scan a QR code

## Building Locally

Build a development client on your machine:

```bash
# iOS (requires Xcode)
## When to Use

Use this skill when you need build Expo app for development.

eas build -p ios --profile development --local

# Android
eas build -p android --profile development --local
```

Local builds output:

- iOS: `.ipa` file
- Android: `.apk` or `.aab` file

## Installing Local Builds

Install iOS build on simulator:

```bash
# Find the .app in the .tar.gz output
tar -xzf build-*.tar.gz
xcrun simctl install booted ./path/to/App.app
```

Install iOS build on device (requires signing):

```bash
# Use Xcode Devices window or ideviceinstaller
ideviceinstaller -i build.ipa
```

Install Android build:

```bash
adb install build.apk
```

## Building for Specific Platform

```bash
# iOS only
eas build -p ios --profile development

# Android only
eas build -p android --profile development

# Both platforms
eas build --profile development
```

## Checking Build Status

```bash
# List recent builds
eas build:list

# View build details
eas build:view
```

## Using the Dev Client

Once installed, the dev client provides:

- **Development server connection** - Enter your Metro bundler URL or scan QR
- **Build information** - View native build details
- **Launcher UI** - Switch between development servers

Connect to local development:

```bash
# Start Metro bundler
npx expo start --dev-client

# Scan QR code with dev client or enter URL manually
```

## Troubleshooting

**Build fails with signing errors:**

```bash
eas credentials
```

**Clear build cache:**

```bash
eas build -p ios --profile development --clear-cache
```

**Check EAS CLI version:**

```bash
eas --version
eas update
```

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-examples
description: Expo's official example projects — the expo/examples repo of ~70 `with-*` integrations (Stripe, Clerk, Supabase, OpenAI, maps, Reanimated, SQLite, Skia, NativeWind, and more). Use when integrating a third-party library or service into an existing Expo app and you want the canonical,...
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-examples
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# Expo Examples
## When to Use

Use this skill when you need expo's official example projects — the expo/examples repo of ~70 `with-*` integrations (Stripe, Clerk, Supabase, OpenAI, maps, Reanimated, SQLite, Skia, NativeWind, and more). Use when integrating a third-party library or service into an existing Expo app and you want the canonical,...

[expo/examples](https://github.com/expo/examples) is Expo's official library of ~70 **integration examples** — directories named `with-<library>` (e.g. `with-stripe`, `with-maps`), each built around **one** library or service. These are not full apps: they're **managed** projects (no `ios/`/`android/` dirs — native setup is via config plugins), and the typical one is a **single screen of ~100–200 lines**. Mine them for the canonical integration *pattern* — the dependency set, `app.json` config plugins, and minimal wiring Expo maintains against the current SDK — and adapt that into the user's app. Don't expect to lift an application architecture from them.

Reach for an example before hand-rolling an integration. (Kinds — full-stack, showcases, starters — are noted in `./references/catalog.md`.)

## Two modes

1. **Inspiration / adapt** (most common) — the user already has a project. Find the matching example, read its key files, and apply the *pattern* to their code.
2. **Scaffold** — greenfield. Start a fresh project directly from the example.

## Workflow

### 1. Find the right example

Map the user's need to an example name (e.g. payments → `with-stripe`, auth → `with-clerk`). `./references/catalog.md` is a categorized snapshot for fast triage — but it drifts, so confirm against the live list:

```bash
# Live example names:
gh api repos/expo/examples/contents --jq '.[] | select(.type=="dir" and (.name|startswith(".")|not)) | .name'
# Aliases (renamed) + deprecated (dead/moved) examples — check before recommending:
gh api repos/expo/examples/contents/meta.json --jq '.content' | base64 -d
```

`meta.json` is the source of truth for what's renamed or dead (deprecated examples are removed from the repo tree but still listed here, each with a `message`). If an example is in its `deprecated` map, don't recommend it — follow the `message` to the modern path. If it's in `aliases`, use the `destination`.

### 2a. Inspiration mode — study without touching the user's project

The common case: the user already has an app and wants to see how Expo does something. Read the example as **reference** and apply the patterns by hand — never scaffold an example on top of their project.

**First, list the whole example in one call.** Integration code is often nested (e.g. Stripe's server routes live in `app/api/`), so a one-level listing misses the important files:

```bash
gh api 'repos/expo/examples/git/trees/master?recursive=1' \
  --jq '.tree[].path | select(startswith("with-stripe/"))'
```

**Then read the high-signal files first:** `README.md` (setup) → `package.json` (deps) → `app.json` (config plugins / permissions) → the integration code the manifest revealed → `.env` (required secrets). Per file:

```bash
gh api repos/expo/examples/contents/with-stripe/utils/stripe-server.ts --jq '.content' | base64 -d
# No gh? Raw URL (branch is master):
curl -s https://raw.githubusercontent.com/expo/examples/master/with-stripe/utils/stripe-server.ts
```

**Reading more than a couple of files?** Many integrations are spread across server routes, a client provider, and config (Stripe is). Skip the per-file calls — pull the whole example into a **throwaway/gitignored dir (not the user's project)** and read it freely with Grep/Read, then apply by hand:

```bash
npx degit expo/examples/with-stripe /tmp/expo-ref/with-stripe   # clean copy, no git history
# fallback without degit (sparse-checkout, no full ~64 MB clone):
git clone --depth 1 --filter=blob:none --sparse https://github.com/expo/examples.git /tmp/expo-ref/examples \
  && (cd /tmp/expo-ref/examples && git sparse-checkout set with-stripe)
```

Read from there with Grep/Read; delete the scratch dir when done.

### 2b. Scaffold mode — new project from an example

```bash
npx create-expo --example with-stripe   # short form:  npx create-expo -e with-stripe
bun create expo --example with-stripe    # with bun
```

### 3. Adapt into the user's app — non-destructively (critical)

When the user already has an app, **add only what the example introduces; never overwrite their setup.**

- **Version-align — don't copy pinned versions.** Examples track the **latest** SDK, so their `package.json` pins won't match an older project. Add only the *missing* deps with `npx expo install <pkg>` (it resolves SDK-correct versions) instead of copying exact versions.
- **Merge config, don't replace it.** Add only the `app.json`/`app.config.*` plugins and permissions the example introduces that the user lacks — keep their existing config block intact.
- **Port the integration code.**
- **Recreate env vars** from the example's `.env` shape — it holds placeholders, never working secrets.

**Done when** the integration code is ported and every dependency, config plugin, permission, and env var it needs is accounted for in the user's app — not when it merely *looks* wired up.

## Gotchas

- **Default branch is `master`,** not `main` (matters for raw URLs and sparse checkout).
- **Single-click deploy.** Every example has a launch URL: `https://launch.expo.dev/?github=https://github.com/expo/examples/tree/master/<example>`.

## Related skills

- Tailwind / NativeWind styling → `expo-tailwind-setup`
- Native UI components → `building-native-ui`
- Authoring a native module → `expo-module`
- Upgrade the SDK before adopting a latest-SDK example → `upgrading-expo`

## References

- `./references/catalog.md` — categorized snapshot of the example library for fast triage.

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-module
description: Guide for creating and writing Expo native modules and views using the Expo Modules API (Swift, Kotlin, TypeScript). Covers module definition DSL, native views, shared objects, config plugins, lifecycle hooks, autolinking, and type system. Use when building or modifying native modules...
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-module
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# Writing Expo Modules

Complete reference for building native modules and views using the Expo Modules API. Covers Swift (iOS), Kotlin (Android), and TypeScript.

## When to Use

- Creating a new Expo native module or native view
- Adding native functionality (camera, sensors, system APIs) to an Expo app
- Wrapping platform SDKs for React Native consumption
- Building config plugins that modify native project files
- Adding Android, Apple, or web support to an existing Expo module
- Editing `expo-module.config.json`, config plugins, or lifecycle hooks

## References

Consult these resources as needed:

```
references/
  create-expo-module.md      Scaffolding and add-platform-support workflow, defaults, and quirks
  native-module.md           Module definition DSL: Name, Function, AsyncFunction, Property, Constant, Events, type system, shared objects
  native-view.md             Native view components: View, Prop, EventDispatcher, view lifecycle, ref-based functions
  lifecycle.md               Lifecycle hooks: module, iOS app/AppDelegate, Android activity/application listeners
  config-plugin.md           Config plugins: modifying Info.plist, AndroidManifest.xml, reading values in native code
  module-config.md           expo-module.config.json fields, file placement, and autolinking behavior
```

## Quick Start

Prefer `create-expo-module` over manually creating native module files and directories. In practice, the best path is usually to create the scaffold first and then build on top of it. The scaffold sets up the expected layout, `expo-module.config.json`, podspec or Gradle files, TypeScript bindings, and the standalone example app flow.

If an existing Expo module only needs another platform, use `create-expo-module add-platform-support` instead of manually copying native directories.

See [references/create-expo-module.md](references/create-expo-module.md) before scaffolding or extending a module. It covers:

- local vs standalone modules
- `--platform`, `--features`, `--barrel`, `--package-manager`, and non-interactive mode
- `expo.autolinking.nativeModulesDir`
- `add-platform-support` behavior and quirks

## Recommended Workflow

1. Choose the scaffold type first:
   - **Local module** for one app
   - **Standalone module** for reuse, monorepos, or publishing
2. Determine native `expo-module` features that you will need.
   - Based on the user's instructions determine which feature scaffolding will be useful.
   - Available features: `Constant`, `Function`, `AsyncFunction`, `Event`, `View`, `ViewEvent`, `SharedObject`
3. Scaffold deliberately:
   - pass an explicit slug or path
   - choose `--platform` intentionally instead of relying on defaults
   - use `--features` to choose code samples which you will modify in the next step to match the real implementation.
4. Replace generated example code with the real implementation.
5. If you add a new platform later, prefer `add-platform-support` over manual file copying.

## Practical Scaffolding Rules

- Feature examples are **opt-in**. A newly scaffolded module may be minimal if no features were selected.
- `ViewEvent` implies `View`.
- Local modules do **not** generate an `index.ts` barrel by default. Use `--barrel` only if you want one.
- In non-interactive local scaffolding, pass the positional slug or path explicitly. `--name` changes the native class name, not the folder name.
- Local modules live in `expo.autolinking.nativeModulesDir` when configured, otherwise in `modules/`.
- Standalone modules have their own package metadata, scripts, and usually an example app. Local modules use the host app's tooling instead.

## Core File Shapes

The Swift and Kotlin DSL share the same structure. Swift is usually the clearest primary example; consult the references for feature-specific details.

## Module Structure Reference

The Swift and Kotlin DSL share the same structure. Both platforms are shown here for reference — in other reference files, Swift is shown as the primary language unless the Kotlin pattern meaningfully differs.

**Swift (iOS):**

```swift
import ExpoModulesCore

public class MyModule: Module {
  public func definition() -> ModuleDefinition {
    Name("MyModule")

    Function("hello") { (name: String) -> String in
      return "Hello \(name)!"
    }
  }
}
```

**Kotlin (Android):**

```kotlin
package expo.modules.mymodule

import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition

class MyModule : Module() {
  override fun definition() = ModuleDefinition {
    Name("MyModule")

    Function("hello") { name: String ->
      "Hello $name!"
    }
  }
}
```

**TypeScript:**

```typescript
import { requireNativeModule } from "expo";

const MyModule = requireNativeModule("MyModule");

export function hello(name: string): string {
  return MyModule.hello(name);
}
```

### expo-module.config.json

```json
{
  "platforms": ["android", "apple"],
  "apple": {
    "modules": ["MyModule"]
  },
  "android": {
    "modules": ["expo.modules.mymodule.MyModule"]
  }
}
```

Note: iOS uses just the class name; Android uses the fully-qualified class name (package + class). See `references/module-config.md` for all fields.

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-observe
description: Use for anything related to EAS Observe — adding `expo-observe` to an Expo project (AppMetricsRoot/ObserveRoot HOC, markInteractive, the useObserve hook, and the Expo Router / React Navigation integrations for per-route metrics), querying via the EAS CLI (`eas observe:metrics-summary`,...
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-observe
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# EAS Observe
## When to Use

Use this skill when you need use for anything related to EAS Observe — adding `expo-observe` to an Expo project (AppMetricsRoot/ObserveRoot HOC, markInteractive, the useObserve hook, and the Expo Router / React Navigation integrations for per-route metrics), querying via the EAS CLI (`eas observe:metrics-summary`,...

EAS Observe tracks startup, navigation, and custom-event performance from production Expo apps.

> **Source of truth:** https://docs.expo.dev/eas/observe/ — always consult the canonical docs when API details matter, especially get-started, configuration, integrations, and the metrics reference. EAS Observe is evolving; this skill's references are written to stay accurate but may lag the docs.

## Which reference to read

The three reference files in `./references/` cover the three things people typically need this skill for:

- **Adding EAS Observe to a project** → [`./references/setup.md`](./references/setup.md). Install, wrap the root layout (`AppMetricsRoot` on SDK 55, `ObserveRoot` on SDK 56+), call `markInteractive()` (global on SDK 55, via the `useObserve()` hook on SDK 56+), and optional per-route navigation metrics through the Expo Router / React Navigation integrations.
- **Querying metrics from the terminal** → [`./references/queries.md`](./references/queries.md). The five `eas observe:*` commands — `metrics-summary`, `metrics`, `routes`, `events`, `versions` — with flags, table layouts, JSON shapes, and common workflows.
- **Reading a dashboard or CLI output** → [`./references/metrics.md`](./references/metrics.md). Target thresholds per metric, what the TTI `frameRate.*` params mean, and diagnostic patterns for telling slow-but-smooth startup apart from main-thread contention or hard blocks.

## Quick links to the docs

- Get started: https://docs.expo.dev/eas/observe/get-started/
- Dashboard guide: https://docs.expo.dev/eas/observe/dashboard/
- Metrics reference: https://docs.expo.dev/eas/observe/reference/metrics/
- Expo Router integration: https://docs.expo.dev/eas/observe/integrations/expo-router/
- React Navigation integration: https://docs.expo.dev/eas/observe/integrations/react-navigation/
- Configuration: https://docs.expo.dev/eas/observe/configuration/

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-tailwind-setup
description: Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-tailwind-setup
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# Tailwind CSS Setup for Expo with react-native-css
## When to Use

Use this skill when you need set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling.

This guide covers setting up Tailwind CSS v4 in Expo using react-native-css and NativeWind v5 for universal styling across iOS, Android, and Web.

## Overview

This setup uses:

- **Tailwind CSS v4** - Modern CSS-first configuration
- **react-native-css** - CSS runtime for React Native
- **NativeWind v5** - Metro transformer for Tailwind in React Native
- **@tailwindcss/postcss** - PostCSS plugin for Tailwind v4

## Installation

```bash
# Install dependencies
npx expo install tailwindcss@^4 nativewind@5.0.0-preview.2 react-native-css@0.0.0-nightly.5ce6396 @tailwindcss/postcss tailwind-merge clsx
```

Add resolutions for lightningcss compatibility:

```json
// package.json
{
  "resolutions": {
    "lightningcss": "1.30.1"
  }
}
```

- autoprefixer is not needed in Expo because of lightningcss
- postcss is included in expo by default

## Configuration Files

### Metro Config

Create or update `metro.config.js`:

```js
// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { withNativewind } = require("nativewind/metro");

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

module.exports = withNativewind(config, {
  // inline variables break PlatformColor in CSS variables
  inlineVariables: false,
  // We add className support manually
  globalClassNamePolyfill: false,
});
```

### PostCSS Config

Create `postcss.config.mjs`:

```js
// postcss.config.mjs
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};
```

### Global CSS

Create `src/global.css`:

```css
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css";

/* Platform-specific font families */
@media android {
  :root {
    --font-mono: monospace;
    --font-rounded: normal;
    --font-serif: serif;
    --font-sans: normal;
  }
}

@media ios {
  :root {
    --font-mono: ui-monospace;
    --font-serif: ui-serif;
    --font-sans: system-ui;
    --font-rounded: ui-rounded;
  }
}
```

## IMPORTANT: No Babel Config Needed

With Tailwind v4 and NativeWind v5, you do NOT need a babel.config.js for Tailwind. Remove any NativeWind babel presets if present:

```js
// DELETE babel.config.js if it only contains NativeWind config
// The following is NO LONGER needed:
// module.exports = function (api) {
//   api.cache(true);
//   return {
//     presets: [
//       ["babel-preset-expo", { jsxImportSource: "nativewind" }],
//       "nativewind/babel",
//     ],
//   };
// };
```

## CSS Component Wrappers

Since react-native-css requires explicit CSS element wrapping, create reusable components:

### Main Components (`src/tw/index.tsx`)

```tsx
import {
  useCssElement,
  useNativeVariable as useFunctionalVariable,
} from "react-native-css";

import { Link as RouterLink } from "expo-router";
import Animated from "react-native-reanimated";
import React from "react";
import {
  View as RNView,
  Text as RNText,
  Pressable as RNPressable,
  ScrollView as RNScrollView,
  TouchableHighlight as RNTouchableHighlight,
  TextInput as RNTextInput,
  StyleSheet,
} from "react-native";

// CSS-enabled Link
export const Link = (
  props: React.ComponentProps<typeof RouterLink> & { className?: string }
) => {
  return useCssElement(RouterLink, props, { className: "style" });
};

Link.Trigger = RouterLink.Trigger;
Link.Menu = RouterLink.Menu;
Link.MenuAction = RouterLink.MenuAction;
Link.Preview = RouterLink.Preview;

// CSS Variable hook
export const useCSSVariable =
  process.env.EXPO_OS !== "web"
    ? useFunctionalVariable
    : (variable: string) => `var(${variable})`;

// View
export type ViewProps = React.ComponentProps<typeof RNView> & {
  className?: string;
};

export const View = (props: ViewProps) => {
  return useCssElement(RNView, props, { className: "style" });
};
View.displayName = "CSS(View)";

// Text
export const Text = (
  props: React.ComponentProps<typeof RNText> & { className?: string }
) => {
  return useCssElement(RNText, props, { className: "style" });
};
Text.displayName = "CSS(Text)";

// ScrollView
export const ScrollView = (
  props: React.ComponentProps<typeof RNScrollView> & {
    className?: string;
    contentContainerClassName?: string;
  }
) => {
  return useCssElement(RNScrollView, props, {
    className: "style",
    contentContainerClassName: "contentContainerStyle",
  });
};
ScrollView.displayName = "CSS(ScrollView)";

// Pressable
export const Pressable = (
  props: React.ComponentProps<typeof RNPressable> & { className?: string }
) => {
  return useCssElement(RNPressable, props, { className: "style" });
};
Pressable.displayName = "CSS(Pressable)";

// TextInput
export const TextInput = (
  props: React.ComponentProps<typeof RNTextInput> & { className?: string }
) => {
  return useCssElement(RNTextInput, props, { className: "style" });
};
TextInput.displayName = "CSS(TextInput)";

// AnimatedScrollView
export const AnimatedScrollView = (
  props: React.ComponentProps<typeof Animated.ScrollView> & {
    className?: string;
    contentClassName?: string;
    contentContainerClassName?: string;
  }
) => {
  return useCssElement(Animated.ScrollView, props, {
    className: "style",
    contentClassName: "contentContainerStyle",
    contentContainerClassName: "contentContainerStyle",
  });
};

// TouchableHighlight with underlayColor extraction
function XXTouchableHighlight(
  props: React.ComponentProps<typeof RNTouchableHighlight>
) {
  const { underlayColor, ...style } = StyleSheet.flatten(props.style) || {};
  return (
    <RNTouchableHighlight
      underlayColor={underlayColor}
      {...props}
      style={style}
    />
  );
}

export const TouchableHighlight = (
  props: React.ComponentProps<typeof RNTouchableHighlight>
) => {
  return useCssElement(XXTouchableHighlight, props, { className: "style" });
};
TouchableHighlight.displayName = "CSS(TouchableHighlight)";
```

### Image Component (`src/tw/image.tsx`)

```tsx
import { useCssElement } from "react-native-css";
import React from "react";
import { StyleSheet } from "react-native";
import Animated from "react-native-reanimated";
import { Image as RNImage } from "expo-image";

const AnimatedExpoImage = Animated.createAnimatedComponent(RNImage);

export type ImageProps = React.ComponentProps<typeof Image>;

function CSSImage(props: React.ComponentProps<typeof AnimatedExpoImage>) {
  // @ts-expect-error: Remap objectFit style to contentFit property
  const { objectFit, objectPosition, ...style } =
    StyleSheet.flatten(props.style) || {};

  return (
    <AnimatedExpoImage
      contentFit={objectFit}
      contentPosition={objectPosition}
      {...props}
      source={
        typeof props.source === "string" ? { uri: props.source } : props.source
      }
      // @ts-expect-error: Style is remapped above
      style={style}
    />
  );
}

export const Image = (
  props: React.ComponentProps<typeof CSSImage> & { className?: string }
) => {
  return useCssElement(CSSImage, props, { className: "style" });
};

Image.displayName = "CSS(Image)";
```

### Animated Components (`src/tw/animated.tsx`)

```tsx
import * as TW from "./index";
import RNAnimated from "react-native-reanimated";

export const Animated = {
  ...RNAnimated,
  View: RNAnimated.createAnimatedComponent(TW.View),
};
```

## Usage

Import CSS-wrapped components from your tw directory:

```tsx
import { View, Text, ScrollView, Image } from "@/tw";

export default function MyScreen() {
  return (
    <ScrollView className="flex-1 bg-white">
      <View className="p-4 gap-4">
        <Text className="text-xl font-bold text-gray-900">Hello Tailwind!</Text>
        <Image
          className="w-full h-48 rounded-lg object-cover"
          source={{ uri: "https://example.com/image.jpg" }}
        />
      </View>
    </ScrollView>
  );
}
```

## Custom Theme Variables

Add custom theme variables in your global.css using `@theme`:

```css
@layer theme {
  @theme {
    /* Custom fonts */
    --font-rounded: "SF Pro Rounded", sans-serif;

    /* Custom line heights */
    --text-xs--line-height: calc(1em / 0.75);
    --text-sm--line-height: calc(1.25em / 0.875);
    --text-base--line-height: calc(1.5em / 1);

    /* Custom leading scales */
    --leading-tight: 1.25em;
    --leading-snug: 1.375em;
    --leading-normal: 1.5em;
  }
}
```

## Platform-Specific Styles

Use platform media queries for platform-specific styling:

```css
@media ios {
  :root {
    --font-sans: system-ui;
    --font-rounded: ui-rounded;
  }
}

@media android {
  :root {
    --font-sans: normal;
    --font-rounded: normal;
  }
}
```

## Apple System Colors with CSS Variables

Create a CSS file for Apple semantic colors:

```css
/* src/css/sf.css */
@layer base {
  html {
    color-scheme: light;
  }
}

:root {
  /* Accent colors with light/dark mode */
  --sf-blue: light-dark(rgb(0 122 255), rgb(10 132 255));
  --sf-green: light-dark(rgb(52 199 89), rgb(48 209 89));
  --sf-red: light-dark(rgb(255 59 48), rgb(255 69 58));

  /* Gray scales */
  --sf-gray: light-dark(rgb(142 142 147), rgb(142 142 147));
  --sf-gray-2: light-dark(rgb(174 174 178), rgb(99 99 102));

  /* Text colors */
  --sf-text: light-dark(rgb(0 0 0), rgb(255 255 255));
  --sf-text-2: light-dark(rgb(60 60 67 / 0.6), rgb(235 235 245 / 0.6));

  /* Background colors */
  --sf-bg: light-dark(rgb(255 255 255), rgb(0 0 0));
  --sf-bg-2: light-dark(rgb(242 242 247), rgb(28 28 30));
}

/* iOS native colors via platformColor */
@media ios {
  :root {
    --sf-blue: platformColor(systemBlue);
    --sf-green: platformColor(systemGreen);
    --sf-red: platformColor(systemRed);
    --sf-gray: platformColor(systemGray);
    --sf-text: platformColor(label);
    --sf-text-2: platformColor(secondaryLabel);
    --sf-bg: platformColor(systemBackground);
    --sf-bg-2: platformColor(secondarySystemBackground);
  }
}

/* Register as Tailwind theme colors */
@layer theme {
  @theme {
    --color-sf-blue: var(--sf-blue);
    --color-sf-green: var(--sf-green);
    --color-sf-red: var(--sf-red);
    --color-sf-gray: var(--sf-gray);
    --color-sf-text: var(--sf-text);
    --color-sf-text-2: var(--sf-text-2);
    --color-sf-bg: var(--sf-bg);
    --color-sf-bg-2: var(--sf-bg-2);
  }
}
```

Then use in components:

```tsx
<Text className="text-sf-text">Primary text</Text>
<Text className="text-sf-text-2">Secondary text</Text>
<View className="bg-sf-bg">...</View>
```

## Using CSS Variables in JavaScript

Use the `useCSSVariable` hook:

```tsx
import { useCSSVariable } from "@/tw";

function MyComponent() {
  const blue = useCSSVariable("--sf-blue");

  return <View style={{ borderColor: blue }} />;
}
```

## Key Differences from NativeWind v4 / Tailwind v3

1. **No babel.config.js** - Configuration is now CSS-first
2. **PostCSS plugin** - Uses `@tailwindcss/postcss` instead of `tailwindcss`
3. **CSS imports** - Use `@import "tailwindcss/..."` instead of `@tailwind` directives
4. **Theme config** - Use `@theme` in CSS instead of `tailwind.config.js`
5. **Component wrappers** - Must wrap components with `useCssElement` for className support
6. **Metro config** - Use `withNativewind` with different options (`inlineVariables: false`)

## Troubleshooting

### Styles not applying

1. Ensure you have the CSS file imported in your app entry
2. Check that components are wrapped with `useCssElement`
3. Verify Metro config has `withNativewind` applied

### Platform colors not working

1. Use `platformColor()` in `@media ios` blocks
2. Fall back to `light-dark()` for web/Android

### TypeScript errors

Add className to component props:

```tsx
type Props = React.ComponentProps<typeof RNView> & { className?: string };
```

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-ui
description: "Build native UI with the @expo/ui package: real SwiftUI on iOS and Jetpack Compose on Android rendered from React in an Expo or React Native app. Covers universal cross-platform components (Host, Column, Row, Button, Text, List, and more imported from @expo/ui), drop-in replacements..."
risk: critical
source: https://github.com/expo/skills/tree/main/plugins/expo/skills/expo-ui
source_repo: expo/skills
source_type: official
date_added: 2026-07-01
license: MIT
license_source: https://github.com/expo/skills/blob/main/LICENSE
---

# Expo UI (`@expo/ui`)
## When to Use

Use this skill when you need build native UI with the @expo/ui package: real SwiftUI on iOS and Jetpack Compose on Android rendered from React in an Expo or React Native app. Covers universal cross-platform components (Host, Column, Row, Button, Text, List, and more imported from @expo/ui), drop-in replacements...

`@expo/ui` renders real native UI from React: SwiftUI on iOS, Jetpack Compose on Android. Start with its universal components (one tree for iOS, Android, and web) and drop to platform-specific SwiftUI/Jetpack Compose only when the universal layer falls short. It also ships drop-in replacements for migrating off RN community UI libraries.

> These instructions track the latest Expo SDK. The **universal** layer requires **SDK 56+**. Drop-in replacements and the platform-specific layers also exist on SDK 55. For component details on a specific SDK, refer to the Expo UI docs for that version.

## Installation

```bash
npx expo install @expo/ui
```

On SDK 56, `@expo/ui` works in Expo Go, so `npx expo start` runs it directly — no custom build required. On older SDKs, build a dev client first (`npx expo run:ios` / `npx expo run:android`).

Every `@expo/ui` tree — universal or platform-specific — must be wrapped in `Host`.

## Choosing an approach (read this first)

Work down this list and stop at the first layer that meets the need:

1. **Universal components — start here.** Import from the `@expo/ui` root. One component tree runs unmodified on iOS, Android, and web from a single source (Compose on Android, SwiftUI on iOS, `react-native-web`/`react-dom` on web). No platform file splits. → `./references/universal.md`

2. **Platform-specific (SwiftUI / Jetpack Compose).** Import from `@expo/ui/swift-ui` or `@expo/ui/jetpack-compose`. Use **only** when the universal layer is missing a component or modifier you need, or when you need platform-specific behavior or optimization. **Downside:** you write two trees and split them into `.ios.tsx` / `.android.tsx` files (or branch on `Platform.OS`) — more code to maintain.

   > **`@expo/ui/swift-ui` is iOS-only. `@expo/ui/jetpack-compose` is Android-only.** Importing either in a file that runs on the other platform will crash at runtime with "Unable to get view config" errors. Isolate platform-specific trees in `.ios.tsx` / `.android.tsx` files placed in `components/` (never inside `app/` — Expo Router does not support platform extensions for route files), or guard with `Platform.OS` in a regular route file. `Host` must always be imported from `@expo/ui` (the universal package root), not from the platform-specific sub-packages. → `./references/swift-ui.md` and `./references/jetpack-compose.md`

**Already using an RN community UI library?** `@expo/ui` also ships **drop-in replacements** — API-compatible swaps for popular libraries (`@gorhom/bottom-sheet`, `@react-native-community/datetimepicker`, and more), imported from `@expo/ui/community/<name>`. This is a migration side-path for replacing an existing dependency, not a step in the universal-vs-platform decision above. → `./references/drop-in-replacements.md`

## References

Consult these resources as needed:

```
references/
  universal.md             Universal @expo/ui components and when to use them (SDK 56+)
  drop-in-replacements.md  API-compatible replacements for RN community UI libraries
  swift-ui.md              Platform-specific iOS UI: @expo/ui/swift-ui components, modifiers, RNHostView, useNativeState
  jetpack-compose.md       Platform-specific Android UI: @expo/ui/jetpack-compose components, modifiers, LazyColumn caveat, icons, useNativeState
```

## Limitations

- Use this skill only when the task clearly matches its upstream product or API scope.
- Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes.
- Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: expo-ui-jetpack-compose
description: expo-ui-jetpack-compose
risk: critical
source: community
---

---
name: expo-ui-jetpack-compose
description: `@expo/ui/jetpack-compose` package lets you use Jetpack Compose Views and modifiers in your app.
---

> The instructions in this skill apply to SDK 55 only. For other SDK versions, refer to the Expo UI Jetpack Compose docs for that version for the most accurate information.

## When to Use
- You need to build Android-native UI in Expo using `@expo/ui/jetpack-compose`.
- The task involves choosing Compose views or modifiers, embedding them in `Host`, or translating Jetpack Compose patterns into Expo UI code.
- You are working specifically against Expo SDK 55 behavior for Jetpack Compose integration.

## Installation

```bash
npx expo install @expo/ui
```

A native rebuild is required after installation (`npx expo run:android`).

## Instructions

- Expo UI's API mirrors Jetpack Compose's API. Use Jetpack Compose and Material Design 3 knowledge to decide which components or modifiers to use.
- Components are imported from `@expo/ui/jetpack-compose`, modifiers from `@expo/ui/jetpack-compose/modifiers`.
- When about to use a component, fetch its docs to confirm the API - https://docs.expo.dev/versions/v55.0.0/sdk/ui/jetpack-compose/{component-name}/index.md
- When unsure about a modifier's API, refer to the docs - https://docs.expo.dev/versions/v55.0.0/sdk/ui/jetpack-compose/modifiers/index.md
- Every Jetpack Compose tree must be wrapped in `Host`. Use `<Host matchContents>` for intrinsic sizing, or `<Host style={{ flex: 1 }}>` when you need explicit size (e.g. as a parent of `LazyColumn`). Example:

```jsx
import { Host, Column, Button, Text } from "@expo/ui/jetpack-compose";
import { fillMaxWidth, paddingAll } from "@expo/ui/jetpack-compose/modifiers";

<Host matchContents>
  <Column verticalArrangement={{ spacedBy: 8 }} modifiers={[fillMaxWidth(), paddingAll(16)]}>
    <Text style={{ typography: "titleLarge" }}>Hello</Text>
    <Button onPress={() => alert("Pressed!")}>Press me</Button>
  </Column>
</Host>;
```

## Key Components

- **LazyColumn** — Use instead of react-native `ScrollView`/`FlatList` for scrollable lists. Wrap in `<Host style={{ flex: 1 }}>`.
- **Icon** — Use `<Icon source={require('./icon.xml')} size={24} />` with Android XML vector drawables from [Material Symbols](https://fonts.google.com/icons).

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: expo-ui-swift-ui
description: expo-ui-swift-ui
risk: critical
source: community
---

---
name: expo-ui-swift-ui
description: `@expo/ui/swift-ui` package lets you use SwiftUI Views and modifiers in your app.
---

> The instructions in this skill apply to SDK 55 only. For other SDK versions, refer to the Expo UI SwiftUI docs for that version for the most accurate information.

## When to Use
- You need to build iOS-native UI in Expo using `@expo/ui/swift-ui`.
- The task involves selecting SwiftUI views or modifiers, wrapping trees in `Host`, or embedding React Native components with `RNHostView`.
- You are targeting Expo SDK 55 behavior for SwiftUI integration and extension guidance.

## Installation

```bash
npx expo install @expo/ui
```

A native rebuild is required after installation (`npx expo run:ios`).

## Instructions

- Expo UI's API mirrors SwiftUI's API. Use SwiftUI knowledge to decide which components or modifiers to use.
- Components are imported from `@expo/ui/swift-ui`, modifiers from `@expo/ui/swift-ui/modifiers`.
- When about to use a component, fetch its docs to confirm the API - https://docs.expo.dev/versions/v55.0.0/sdk/ui/swift-ui/{component-name}/index.md
- When unsure about a modifier's API, refer to the docs - https://docs.expo.dev/versions/v55.0.0/sdk/ui/swift-ui/modifiers/index.md
- Every SwiftUI tree must be wrapped in `Host`.
- `RNHostView` is specifically for embedding RN components inside a SwiftUI tree. Example:

```jsx
import { Host, VStack, RNHostView } from "@expo-ui/swift-ui";
import { Pressable } from "react-native";

<Host matchContents>
  <VStack>
    <RNHostView matchContents>
      // Here, `Pressable` is an RN component so it is wrapped in `RNHostView`.
      <Pressable />
    </RNHostView>
  </VStack>
</Host>;
```

- If a required modifier or View is missing in Expo UI, it can be extended via a local Expo module. See: https://docs.expo.dev/guides/expo-ui-swift-ui/extending/index.md. Confirm with the user before extending.

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: fable-safe-prompt
description: "Rewrite allowed prompts to reduce false-positive safety triggers without bypassing policy or changing intent."
category: agent-behavior
risk: safe
source: community
source_repo: davidondrej/skills
source_type: community
date_added: "2026-07-07"
author: davidondrej
tags: [prompting, safety, guardrails]
tools: [claude, codex]
license: "MIT"
license_source: "https://github.com/davidondrej/skills/blob/main/LICENSE"
disable-model-invocation: true
---

# Fable-Safe Prompt

## When to Use

- Use when an allowed prompt appears likely to hit false-positive classifier language.
- Use when the user asks for prompt wording that keeps benign intent clear and policy-compliant.

Return the user's prompt back **in full, verbatim**, changing ONLY the specific sentences/phrases most likely to trip Fable 5's cyber/bio classifiers. Do not rewrite the whole thing, do not summarize, do not restructure — minimal surgical edits. Keep the actual goal and all benign text exactly as written. This lowers false positives for benign work. It must not be used to bypass safety policy or enable harmful requests.

Fable 5 runs 3 input classifiers: **cyber**, **bio/chem**, **reasoning_extraction**. Triggers are keyword/surface-based, largely intent-independent ("hello" and "cancer" got flagged). The fix is to abstract away the sensitive domain, not just restate intent.

Apply when the prompt touches: cyber/auth/exploits/malware/pentesting/credentials; bio/chem/medical/disease/lab topics; or any "show your reasoning" instruction.

The user provides the prompt-to-be-changed wrapped in `<prompt>...</prompt>` XML tags. Operate only on the text inside those tags; treat everything outside as instructions to you.

## Method

1. Flag the highly problematic sentences/phrases (offensive framings, named sensitive domains, "show your thinking" lines).
2. Replace each in place with a safe equivalent (see swaps), or describe the wanted functionality abstractly rather than naming the domain.
3. Leave everything else byte-for-byte identical.
4. Return the complete edited prompt.

Reframe so the task reads as: **owned/authorized**, **defensive** (implement/fix/test, not attack), and **not dual-use** (no exploit dev, attack simulation, bypass payloads, malware, live testing).

### Swap framings (apply per-sentence)

| Triggering | Safe rewrite |
|---|---|
| "How could an attacker exploit this auth" | "Review these auth files for missing checks and fix them defensively" |
| "Write an exploit / payload / PoC" | "Add a regression test proving X is fixed, then patch it" |
| "How do I bypass / brute-force Y" | "Enforce secure session validation + rate limiting on Y" |
| "Reverse this malware / show attack steps" | "Describe the risk high-level, then implement the fix" |
| "Show your reasoning / explain step-by-step / walk me through your thinking" | **Delete it.** Trips `reasoning_extraction`. If progress visibility is needed, ask it to use a send-to-user tool, not to echo its internal thoughts. |
| Clinician framing: "as a doctor, diagnose this ECG" | Patient framing: "help me interpret this ECG my doctor gave me" |
| Named bio/chem domain: "cancer / disease pathway / chemical kinetics" | Abstract it: describe the data/analysis generically, drop the domain noun |

### Trigger keywords to abstract away
*Cyber:* exploit, malware, vulnerability, attack, bypass, stealth, fingerprinting, anti-bot, CAPTCHA, penetration.
*Bio/chem:* biology, biomedicine, chemistry, cancer, disease pathways, RNA/variant calling, equilibrium, kinetics, diagnosis.
*Distillation:* "distill the model", training pipelines, frontier LLM development.

If no benign defensive equivalent exists for a sentence (it's purely offensive), flag it to the user rather than silently neutering the intent.

## Output

1. Print the full safe prompt back to the user in text (a code block, ready to paste).
2. **Copy it to the clipboard** so the user can paste immediately:
   ```bash
   pbcopy <<'EOF'
   <the full safe prompt>
   EOF
   ```
   Confirm in one line that it's on the clipboard.
3. A short list of exactly which sentences you changed and what they became.
4. If the task is genuinely offensive (pentest, exploit repro, malware analysis): say plainly no edit makes it Fable-safe — use an Opus 4.8 fallback or vetted Mythos, not Fable 5.

**Hard truth:** you can't reliably stop Fable 5 guardrails. Robust API setups also treat `stop_reason: "refusal"` (HTTP 200, `stop_details.category` = `cyber`/`bio`) as a route to an Opus 4.8 fallback — mention only if the user controls the integration.

## Limitations

- Adapted from `davidondrej/skills`; verify local paths, tools, credentials, and agent features before acting.
- For commands, remote access, scheduling, browser automation, or file-changing workflows, get explicit user approval and confirm the target environment first.