Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Expo Examples — Integration Skills

Add popular integrations to an Expo project using curated skills from this repository.

## How it works

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.

## Available integrations

| Category | Options | Skill |
|----------|---------|-------|
| **Authentication** | Clerk, Auth0, Better Auth | See [Authentication](#authentication) |
| **Payments** | Stripe (native + web) | `with-stripe` |
| **Database** | SQLite, LibSQL (Turso), Convex | See [Database](#database) |
| **Styling** | TailwindCSS (Nativewind) | `with-tailwindcss` |
| **API Client** | Apollo GraphQL, GraphQL (Yoga + URQL) | See [API Client](#api-client) |
| **Animations** | Reanimated, Moti | See [Animations](#animations) |
| **State Management** | Zustand | `with-zustand` |
| **Device Features** | Camera, Maps | See [Device Features](#device-features) |

## Fetching a skill

Once you know the skill name (e.g. `with-clerk`), fetch its SKILL.md:

```
https://raw.githubusercontent.com/expo/examples/master/with-clerk/SKILL.md
```

Then follow the instructions in the fetched SKILL.md to implement the integration.

---

## Category decision guides

Use these when the user hasn't specified a particular library.

### Authentication

| Option | Best for | Trade-offs |
|--------|----------|------------|
| **Clerk** (recommended) | Quick setup, managed auth with pre-built UI, social login, MFA | Requires Clerk account, usage-based pricing |
| **Auth0** | Enterprise SSO, SAML, compliance requirements | More complex setup, requires Auth0 account |
| **Better Auth** | Full control, self-hosted, open-source with Prisma | More setup (Prisma, database), self-hosted responsibility |

**Quick decision:** Need it fast? → `with-clerk`. Enterprise SSO? → `with-auth0`. Self-hosted control? → `with-better-auth`. Not sure? → `with-clerk`.

### Database

| Option | Best for | Trade-offs |
|--------|----------|------------|
| **SQLite** (recommended) | Local offline-first storage, no server needed | No cloud sync, data stays on device |
| **LibSQL** (Turso) | Local-first with cloud sync | Requires Turso account, sync management |
| **Convex** | Real-time data, collaborative features, full backend | Requires internet, Convex account, vendor lock-in |

**Quick decision:** Offline-only? → `with-sqlite`. Need sync? → `with-libsql`. Real-time? → `with-convex`. Not sure? → `with-sqlite`.

### API Client

| Option | Best for | Trade-offs |
|--------|----------|------------|
| **Apollo Client** | Connecting to an existing external GraphQL API | Client-only, no server included |
| **GraphQL Full-Stack** (Yoga + URQL + gql.tada) | Building a GraphQL API from scratch within the Expo app | More complex, requires server output mode |

**Quick decision:** Have an API? → `with-apollo`. Building one? → `with-graphql`.

### Animations

| Option | Best for | Trade-offs |
|--------|----------|------------|
| **Reanimated** (recommended) | Complex, gesture-driven, performance-critical animations | More verbose API |
| **Moti** | Simple declarative animations, enter/exit transitions, skeleton loading | Less control, adds dependency (requires Reanimated) |

**Quick decision:** Complex/gesture animations? → `with-reanimated`. Simple fade/scale/mount? → `with-moti`. Not sure? → `with-reanimated`.

### Device Features

| Option | What it does |
|--------|-------------|
| **Camera** | Photo capture, video recording, front/back switching via `expo-camera` |
| **Maps** | Interactive maps with markers and overlays via `react-native-maps` |

**Quick decision:** Photos/video? → `with-camera`. Location display? → `with-maps`.

---

## Adaptation rules

These apply to ALL integrations:

- **Merge dependencies** — add to existing `package.json`, never replace it
- **Merge plugins** — add to existing `app.json` plugins array
- **Adapt navigation** — use the project's existing pattern (Expo Router / React Navigation)
- **Match styling** — follow the project's existing styling approach
- **Create, don't overwrite** — add new files, don't replace existing ones
- **Preserve structure** — follow the project's existing directory conventions

## Discovery

To discover additional integration skills beyond those listed above, browse:

```
https://github.com/expo/examples
```

Any `with-*` folder containing a `SKILL.md` is a fetchable integration skill.
167 changes: 167 additions & 0 deletions with-apollo/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
name: with-apollo
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.
version: 1.0.0
license: MIT
---

# Add Apollo GraphQL Client

## When to use

- User wants to connect to an external GraphQL API
- User prefers Apollo Client over URQL
- User needs GraphQL caching and state management

## Dependencies

```bash
npm install @apollo/client graphql
```

Optional for authenticated requests:
```bash
npm install @apollo/link-context
```

## Implementation

### 1. Create Apollo client

Create `utils/apollo.js` (or `.ts`):

```tsx
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client";

const client = new ApolloClient({
link: new HttpLink({
uri: "https://your-graphql-endpoint.com/graphql",
}),
cache: new InMemoryCache(),
});

export default client;
```

### 2. (Optional) Add authentication

```tsx
import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
import { setContext } from "@apollo/link-context";

const httpLink = new HttpLink({
uri: "https://your-graphql-endpoint.com/graphql",
});

const authLink = setContext(async (_, { headers }) => {
const token = await getToken(); // your auth token logic
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
});

const client = new ApolloClient({
link: from([authLink, httpLink]),
cache: new InMemoryCache(),
});

export default client;
```

### 3. Wrap app with ApolloProvider

In root layout or App component:

```tsx
import { ApolloProvider } from "@apollo/client";
import client from "@/utils/apollo";

export default function RootLayout() {
return (
<ApolloProvider client={client}>
{/* rest of the app */}
</ApolloProvider>
);
}
```

### 4. Define and use queries

```tsx
import { gql, useQuery } from "@apollo/client";

const GET_ITEMS = gql`
query GetItems {
items {
id
name
}
}
`;

export default function ItemsScreen() {
const { loading, error, data } = useQuery(GET_ITEMS);

if (loading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;

return (
<FlatList
data={data.items}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
);
}
```

### 5. Mutations

```tsx
import { gql, useMutation } from "@apollo/client";

const CREATE_ITEM = gql`
mutation CreateItem($name: String!) {
createItem(name: $name) {
id
name
}
}
`;

function CreateItemButton() {
const [createItem, { loading }] = useMutation(CREATE_ITEM, {
refetchQueries: [GET_ITEMS],
});

return (
<Button
title="Add Item"
disabled={loading}
onPress={() => createItem({ variables: { name: "New Item" } })}
/>
);
}
```

## Key hooks reference

| Hook | Purpose |
|------|---------|
| `useQuery(query, options?)` | Fetch data, returns `{ loading, error, data }` |
| `useMutation(mutation, options?)` | Execute mutations |
| `useLazyQuery(query)` | Fetch on demand (not on mount) |

## Adaptation notes

- Merge dependencies — don't replace `package.json`
- Replace the GraphQL endpoint URI with the user's actual API
- Add `ApolloProvider` as an outer wrapper in the existing layout
- Apollo works with any GraphQL API — no server-side code needed
- For a full-stack GraphQL setup with Expo Router API routes, see the `with-graphql` skill

## Reference

See full working example in this directory.
101 changes: 101 additions & 0 deletions with-auth0/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
name: with-auth0
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.
version: 1.0.0
license: MIT
---

# Add Auth0 Authentication

## When to use

- User wants Auth0 for authentication
- User needs enterprise SSO or SAML support
- User wants OAuth 2.0 browser-based login flow

## Dependencies

```bash
npx expo install expo-auth-session expo-crypto
npm install jwt-decode
```

## Configuration

### Auth0 Dashboard setup

1. Create an application in the Auth0 Dashboard
2. Set the callback URL to: `https://auth.expo.io/@<username>/<slug>`
(get the exact URL from `AuthSession.makeRedirectUri()`)
3. Note the **Client ID** and **Domain**

## Implementation

### 1. Create auth screen

```tsx
import * as AuthSession from "expo-auth-session";
import { jwtDecode } from "jwt-decode";
import { useState } from "react";
import { Button, Text, View, Platform } from "react-native";

const AUTH0_DOMAIN = "your-tenant.auth0.com";
const AUTH0_CLIENT_ID = "your-client-id";

const redirectUri = AuthSession.makeRedirectUri();
const discovery = AuthSession.useAutoDiscovery(`https://${AUTH0_DOMAIN}`);

export default function LoginScreen() {
const [user, setUser] = useState(null);

const [request, result, promptAsync] = AuthSession.useAuthRequest(
{
redirectUri,
clientId: AUTH0_CLIENT_ID,
responseType: AuthSession.ResponseType.IdToken,
scopes: ["openid", "profile", "email"],
extraParams: {
nonce: "nonce", // Use a random nonce in production
},
},
discovery
);

const handleLogin = async () => {
const response = await promptAsync();
if (response?.type === "success") {
const { id_token } = response.params;
const decoded = jwtDecode(id_token);
setUser(decoded);
}
};

if (user) {
return (
<View>
<Text>Welcome, {user.name}!</Text>
<Button title="Log Out" onPress={() => setUser(null)} />
</View>
);
}

return (
<View>
<Button title="Log In with Auth0" onPress={handleLogin} disabled={!request} />
</View>
);
}
```

## Adaptation notes

- Merge dependencies — don't replace `package.json`
- Replace `AUTH0_DOMAIN` and `AUTH0_CLIENT_ID` with user's Auth0 credentials
- The redirect URI must be added to Auth0's "Allowed Callback URLs"
- Use a cryptographically random nonce in production (not the string `"nonce"`)
- For managed auth with pre-built UI, consider the `with-clerk` skill instead
- `expo-auth-session` handles the browser-based OAuth flow automatically

## Reference

See full working example in this directory.
Loading