Skip to content

Commit 149fb75

Browse files
committed
feat: add AI-discoverable integration skills for 27 example projects
Add SKILL.md guides to example folders enabling AI agents to fetch and follow curated integration instructions via plain HTTP URLs. A master SKILL.md at the root provides a discovery index with category decision guides (auth, database, API client, animations, device features) and fetch instructions for individual skills. Covered integrations: - Auth: Clerk, Auth0, Better Auth - Payments: Stripe - Database: SQLite, LibSQL (Turso), Convex, TinyBase, Legend-State + Supabase - Styling: TailwindCSS, shadcn/ui - API: Apollo GraphQL, GraphQL (Yoga + URQL) - AI: OpenAI, Vercel AI SDK (router-ai) - Animations: Reanimated, Moti - Graphics: Skia, React Three Fiber - State: Zustand - Realtime: Socket.IO - Monitoring: Sentry - Charts: Victory Native - Images: Satori - Device: Camera, Maps - DX: Storybook
1 parent e809459 commit 149fb75

28 files changed

Lines changed: 4407 additions & 0 deletions

File tree

SKILL.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Expo Examples — Integration Skills
2+
3+
Add popular integrations to an Expo project using curated skills from this repository.
4+
5+
## How it works
6+
7+
Each `with-*` example folder contains a `SKILL.md` with complete implementation instructions. Use the table below to find the right skill, then fetch and follow it.
8+
9+
## Available integrations
10+
11+
| Category | Options | Skill |
12+
|----------|---------|-------|
13+
| **Authentication** | Clerk, Auth0, Better Auth | See [Authentication](#authentication) |
14+
| **Payments** | Stripe (native + web) | `with-stripe` |
15+
| **Database** | SQLite, LibSQL (Turso), Convex | See [Database](#database) |
16+
| **Styling** | TailwindCSS (Nativewind) | `with-tailwindcss` |
17+
| **API Client** | Apollo GraphQL, GraphQL (Yoga + URQL) | See [API Client](#api-client) |
18+
| **Animations** | Reanimated, Moti | See [Animations](#animations) |
19+
| **State Management** | Zustand | `with-zustand` |
20+
| **Device Features** | Camera, Maps | See [Device Features](#device-features) |
21+
22+
## Fetching a skill
23+
24+
Once you know the skill name (e.g. `with-clerk`), fetch its SKILL.md:
25+
26+
```
27+
https://raw.githubusercontent.com/expo/examples/master/with-clerk/SKILL.md
28+
```
29+
30+
Then follow the instructions in the fetched SKILL.md to implement the integration.
31+
32+
---
33+
34+
## Category decision guides
35+
36+
Use these when the user hasn't specified a particular library.
37+
38+
### Authentication
39+
40+
| Option | Best for | Trade-offs |
41+
|--------|----------|------------|
42+
| **Clerk** (recommended) | Quick setup, managed auth with pre-built UI, social login, MFA | Requires Clerk account, usage-based pricing |
43+
| **Auth0** | Enterprise SSO, SAML, compliance requirements | More complex setup, requires Auth0 account |
44+
| **Better Auth** | Full control, self-hosted, open-source with Prisma | More setup (Prisma, database), self-hosted responsibility |
45+
46+
**Quick decision:** Need it fast? → `with-clerk`. Enterprise SSO? → `with-auth0`. Self-hosted control? → `with-better-auth`. Not sure? → `with-clerk`.
47+
48+
### Database
49+
50+
| Option | Best for | Trade-offs |
51+
|--------|----------|------------|
52+
| **SQLite** (recommended) | Local offline-first storage, no server needed | No cloud sync, data stays on device |
53+
| **LibSQL** (Turso) | Local-first with cloud sync | Requires Turso account, sync management |
54+
| **Convex** | Real-time data, collaborative features, full backend | Requires internet, Convex account, vendor lock-in |
55+
56+
**Quick decision:** Offline-only? → `with-sqlite`. Need sync? → `with-libsql`. Real-time? → `with-convex`. Not sure? → `with-sqlite`.
57+
58+
### API Client
59+
60+
| Option | Best for | Trade-offs |
61+
|--------|----------|------------|
62+
| **Apollo Client** | Connecting to an existing external GraphQL API | Client-only, no server included |
63+
| **GraphQL Full-Stack** (Yoga + URQL + gql.tada) | Building a GraphQL API from scratch within the Expo app | More complex, requires server output mode |
64+
65+
**Quick decision:** Have an API? → `with-apollo`. Building one? → `with-graphql`.
66+
67+
### Animations
68+
69+
| Option | Best for | Trade-offs |
70+
|--------|----------|------------|
71+
| **Reanimated** (recommended) | Complex, gesture-driven, performance-critical animations | More verbose API |
72+
| **Moti** | Simple declarative animations, enter/exit transitions, skeleton loading | Less control, adds dependency (requires Reanimated) |
73+
74+
**Quick decision:** Complex/gesture animations? → `with-reanimated`. Simple fade/scale/mount? → `with-moti`. Not sure? → `with-reanimated`.
75+
76+
### Device Features
77+
78+
| Option | What it does |
79+
|--------|-------------|
80+
| **Camera** | Photo capture, video recording, front/back switching via `expo-camera` |
81+
| **Maps** | Interactive maps with markers and overlays via `react-native-maps` |
82+
83+
**Quick decision:** Photos/video? → `with-camera`. Location display? → `with-maps`.
84+
85+
---
86+
87+
## Adaptation rules
88+
89+
These apply to ALL integrations:
90+
91+
- **Merge dependencies** — add to existing `package.json`, never replace it
92+
- **Merge plugins** — add to existing `app.json` plugins array
93+
- **Adapt navigation** — use the project's existing pattern (Expo Router / React Navigation)
94+
- **Match styling** — follow the project's existing styling approach
95+
- **Create, don't overwrite** — add new files, don't replace existing ones
96+
- **Preserve structure** — follow the project's existing directory conventions
97+
98+
## Discovery
99+
100+
To discover additional integration skills beyond those listed above, browse:
101+
102+
```
103+
https://github.com/expo/examples
104+
```
105+
106+
Any `with-*` folder containing a `SKILL.md` is a fetchable integration skill.

with-apollo/SKILL.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
---
2+
name: with-apollo
3+
description: Add Apollo Client for GraphQL to an Expo project. Connects to external GraphQL APIs with caching and state management. Use when the user wants Apollo, GraphQL client, or needs to query an external GraphQL endpoint.
4+
version: 1.0.0
5+
license: MIT
6+
---
7+
8+
# Add Apollo GraphQL Client
9+
10+
## When to use
11+
12+
- User wants to connect to an external GraphQL API
13+
- User prefers Apollo Client over URQL
14+
- User needs GraphQL caching and state management
15+
16+
## Dependencies
17+
18+
```bash
19+
npm install @apollo/client graphql
20+
```
21+
22+
Optional for authenticated requests:
23+
```bash
24+
npm install @apollo/link-context
25+
```
26+
27+
## Implementation
28+
29+
### 1. Create Apollo client
30+
31+
Create `utils/apollo.js` (or `.ts`):
32+
33+
```tsx
34+
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client";
35+
36+
const client = new ApolloClient({
37+
link: new HttpLink({
38+
uri: "https://your-graphql-endpoint.com/graphql",
39+
}),
40+
cache: new InMemoryCache(),
41+
});
42+
43+
export default client;
44+
```
45+
46+
### 2. (Optional) Add authentication
47+
48+
```tsx
49+
import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
50+
import { setContext } from "@apollo/link-context";
51+
52+
const httpLink = new HttpLink({
53+
uri: "https://your-graphql-endpoint.com/graphql",
54+
});
55+
56+
const authLink = setContext(async (_, { headers }) => {
57+
const token = await getToken(); // your auth token logic
58+
return {
59+
headers: {
60+
...headers,
61+
authorization: token ? `Bearer ${token}` : "",
62+
},
63+
};
64+
});
65+
66+
const client = new ApolloClient({
67+
link: from([authLink, httpLink]),
68+
cache: new InMemoryCache(),
69+
});
70+
71+
export default client;
72+
```
73+
74+
### 3. Wrap app with ApolloProvider
75+
76+
In root layout or App component:
77+
78+
```tsx
79+
import { ApolloProvider } from "@apollo/client";
80+
import client from "@/utils/apollo";
81+
82+
export default function RootLayout() {
83+
return (
84+
<ApolloProvider client={client}>
85+
{/* rest of the app */}
86+
</ApolloProvider>
87+
);
88+
}
89+
```
90+
91+
### 4. Define and use queries
92+
93+
```tsx
94+
import { gql, useQuery } from "@apollo/client";
95+
96+
const GET_ITEMS = gql`
97+
query GetItems {
98+
items {
99+
id
100+
name
101+
}
102+
}
103+
`;
104+
105+
export default function ItemsScreen() {
106+
const { loading, error, data } = useQuery(GET_ITEMS);
107+
108+
if (loading) return <Text>Loading...</Text>;
109+
if (error) return <Text>Error: {error.message}</Text>;
110+
111+
return (
112+
<FlatList
113+
data={data.items}
114+
renderItem={({ item }) => <Text>{item.name}</Text>}
115+
/>
116+
);
117+
}
118+
```
119+
120+
### 5. Mutations
121+
122+
```tsx
123+
import { gql, useMutation } from "@apollo/client";
124+
125+
const CREATE_ITEM = gql`
126+
mutation CreateItem($name: String!) {
127+
createItem(name: $name) {
128+
id
129+
name
130+
}
131+
}
132+
`;
133+
134+
function CreateItemButton() {
135+
const [createItem, { loading }] = useMutation(CREATE_ITEM, {
136+
refetchQueries: [GET_ITEMS],
137+
});
138+
139+
return (
140+
<Button
141+
title="Add Item"
142+
disabled={loading}
143+
onPress={() => createItem({ variables: { name: "New Item" } })}
144+
/>
145+
);
146+
}
147+
```
148+
149+
## Key hooks reference
150+
151+
| Hook | Purpose |
152+
|------|---------|
153+
| `useQuery(query, options?)` | Fetch data, returns `{ loading, error, data }` |
154+
| `useMutation(mutation, options?)` | Execute mutations |
155+
| `useLazyQuery(query)` | Fetch on demand (not on mount) |
156+
157+
## Adaptation notes
158+
159+
- Merge dependencies — don't replace `package.json`
160+
- Replace the GraphQL endpoint URI with the user's actual API
161+
- Add `ApolloProvider` as an outer wrapper in the existing layout
162+
- Apollo works with any GraphQL API — no server-side code needed
163+
- For a full-stack GraphQL setup with Expo Router API routes, see the `with-graphql` skill
164+
165+
## Reference
166+
167+
See full working example in this directory.

with-auth0/SKILL.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
name: with-auth0
3+
description: Add Auth0 OAuth authentication to an Expo project. Browser-based OAuth 2.0 flow with JWT token decoding. Use when the user wants Auth0, enterprise SSO, SAML, or OAuth login.
4+
version: 1.0.0
5+
license: MIT
6+
---
7+
8+
# Add Auth0 Authentication
9+
10+
## When to use
11+
12+
- User wants Auth0 for authentication
13+
- User needs enterprise SSO or SAML support
14+
- User wants OAuth 2.0 browser-based login flow
15+
16+
## Dependencies
17+
18+
```bash
19+
npx expo install expo-auth-session expo-crypto
20+
npm install jwt-decode
21+
```
22+
23+
## Configuration
24+
25+
### Auth0 Dashboard setup
26+
27+
1. Create an application in the Auth0 Dashboard
28+
2. Set the callback URL to: `https://auth.expo.io/@<username>/<slug>`
29+
(get the exact URL from `AuthSession.makeRedirectUri()`)
30+
3. Note the **Client ID** and **Domain**
31+
32+
## Implementation
33+
34+
### 1. Create auth screen
35+
36+
```tsx
37+
import * as AuthSession from "expo-auth-session";
38+
import { jwtDecode } from "jwt-decode";
39+
import { useState } from "react";
40+
import { Button, Text, View, Platform } from "react-native";
41+
42+
const AUTH0_DOMAIN = "your-tenant.auth0.com";
43+
const AUTH0_CLIENT_ID = "your-client-id";
44+
45+
const redirectUri = AuthSession.makeRedirectUri();
46+
const discovery = AuthSession.useAutoDiscovery(`https://${AUTH0_DOMAIN}`);
47+
48+
export default function LoginScreen() {
49+
const [user, setUser] = useState(null);
50+
51+
const [request, result, promptAsync] = AuthSession.useAuthRequest(
52+
{
53+
redirectUri,
54+
clientId: AUTH0_CLIENT_ID,
55+
responseType: AuthSession.ResponseType.IdToken,
56+
scopes: ["openid", "profile", "email"],
57+
extraParams: {
58+
nonce: "nonce", // Use a random nonce in production
59+
},
60+
},
61+
discovery
62+
);
63+
64+
const handleLogin = async () => {
65+
const response = await promptAsync();
66+
if (response?.type === "success") {
67+
const { id_token } = response.params;
68+
const decoded = jwtDecode(id_token);
69+
setUser(decoded);
70+
}
71+
};
72+
73+
if (user) {
74+
return (
75+
<View>
76+
<Text>Welcome, {user.name}!</Text>
77+
<Button title="Log Out" onPress={() => setUser(null)} />
78+
</View>
79+
);
80+
}
81+
82+
return (
83+
<View>
84+
<Button title="Log In with Auth0" onPress={handleLogin} disabled={!request} />
85+
</View>
86+
);
87+
}
88+
```
89+
90+
## Adaptation notes
91+
92+
- Merge dependencies — don't replace `package.json`
93+
- Replace `AUTH0_DOMAIN` and `AUTH0_CLIENT_ID` with user's Auth0 credentials
94+
- The redirect URI must be added to Auth0's "Allowed Callback URLs"
95+
- Use a cryptographically random nonce in production (not the string `"nonce"`)
96+
- For managed auth with pre-built UI, consider the `with-clerk` skill instead
97+
- `expo-auth-session` handles the browser-based OAuth flow automatically
98+
99+
## Reference
100+
101+
See full working example in this directory.

0 commit comments

Comments
 (0)