encore-frontend — Claude Code skill: wire a frontend to Encore.ts
A skill that connects React, Next.js, Vue or Svelte apps to an Encore.ts backend: typed client generation, CORS setup and auth tokens.
npx skills add encoredev/skills --skill encore-frontend -a claude-code
npx skills add encoredev/skills --skill frontend -a claude-codeWhat encore-frontend does
The skill tells your agent how to hook browser code up to an Encore.ts API. Three things get covered: generating a TypeScript client with encore gen client, configuring CORS in encore.app, and passing an auth token in the Authorization header.
The client is generated per environment — encore gen client --output=./frontend/src/client.ts --env=local for local work, --env=staging for a deployed one. Calls come out typed, like client.user.getUser({ id: "123" }). Examples inside cover React with useState, TanStack Query with useQuery and useMutation, and Next.js server components. Prefer plain requests? There is a fetch section too.
Credentialed requests need allow_origins_with_credentials in encore.app, otherwise the browser blocks them. Keep the API URL in environment variables: VITE_API_URL for Vite, NEXT_PUBLIC_API_URL for Next.js. Regenerate the client whenever your API changes, or the types drift away from the backend.
What it helps with
- generate a typed TypeScript client for a local or staging environment
- set up CORS in
encore.appfor production domains - pass an auth token through the
Authorizationheader - call an Encore endpoint from a React component with loading and error states
- fetch data inside a Next.js server component
Example prompts for the agent
- Generate the Encore TypeScript client and wire it into my React profile component
- Configure CORS in
encore.appso my frontend on localhost:3000 can reach the backend - Show me how to send the auth token with TanStack Query requests
How to install encore-frontend
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill encore-frontend -a claude-codenpx skills add encoredev/skills --skill frontend -a claude-code2Manually: put the files in place
~/.claude/skills/frontend/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/frontend/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill frontend -a codex2Manually: put the files in place
.agents/skills/frontend/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill frontend -a cursor2Manually: put the files in place
.cursor/skills/frontend/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install encoredev/skills frontend2Manually: put the files in place
.github/skills/frontend/SKILL.md1Skills CLI (Vercel)
npx skills add encoredev/skills --skill frontend -a gemini-cli2Platform CLI
gemini skills install https://github.com/encoredev/skills --path encore/frontendManually: put the files in place
.devin/skills/frontend/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add encoredev/skills --skill frontend -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about encore-frontend
How do I install encore-frontend in Claude?
Run npx skills add encoredev/skills --skill encore-frontend -a claude-code. The skill lands in ~/.claude/skills/frontend/SKILL.md.
What does encore-frontend do?
It guides the agent through connecting a React, Next.js, Vue, Svelte, SvelteKit, Remix or Astro frontend to an Encore.ts backend: client generation, CORS, authentication.
Do I have to use the generated client with Encore?
No. The skill also documents plain fetch calls with a response.ok check if you would rather skip code generation.
Source text
SKILL.md · Apache-2.0
---
name: encore-frontend
description: Connect a frontend application (React, Next.js, Vue, Svelte, etc.) to an Encore.ts backend.
when_to_use: >-
User wants to generate a TypeScript API client for the browser (`encore gen client`), call Encore endpoints from React/Next.js/Vue/Svelte/SvelteKit/Remix/Astro, configure CORS in `encore.app`, or wire authentication tokens through a generated client. Trigger phrases: "TypeScript client", "API client", "Next.js frontend", "React frontend", "encore gen client", "CORS", "browser fetch", "SPA", "frontend can call this backend".
---
# Frontend Integration with Encore
## Instructions
Encore provides tools to connect your frontend applications to your backend APIs.
### Generate a TypeScript Client
```bash
# Generate client for local development
encore gen client --output=./frontend/src/client.ts --env=local
# Generate client for a deployed environment
encore gen client --output=./frontend/src/client.ts --env=staging
```
This generates a fully typed client based on your API definitions.
### Using the Generated Client
```typescript
// frontend/src/client.ts is auto-generated
import Client from "./client";
const client = new Client("http://localhost:4000");
// Fully typed API calls
const user = await client.user.getUser({ id: "123" });
console.log(user.email);
const newUser = await client.user.createUser({
email: "[email protected]",
name: "New User",
});
```
### React Example
```tsx
// frontend/src/components/UserProfile.tsx
import { useState, useEffect } from "react";
import Client from "../client";
const client = new Client(import.meta.env.VITE_API_URL);
export function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
client.user.getUser({ id: userId })
.then(setUser)
.catch(setError)
.finally(() => setLoading(false));
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
```
### React with TanStack Query
```tsx
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import Client from "../client";
const client = new Client(import.meta.env.VITE_API_URL);
export function UserProfile({ userId }: { userId: string }) {
const { data: user, isLoading, error } = useQuery({
queryKey: ["user", userId],
queryFn: () => client.user.getUser({ id: userId }),
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{user.name}</div>;
}
export function CreateUserForm() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (data: { email: string; name: string }) =>
client.user.createUser(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
},
});
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
mutation.mutate({
email: formData.get("email") as string,
name: formData.get("name") as string,
});
};
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" required />
<input name="name" required />
<button type="submit" disabled={mutation.isPending}>
{mutation.isPending ? "Creating..." : "Create User"}
</button>
</form>
);
}
```
### Next.js Server Components
```tsx
// app/users/[id]/page.tsx
import Client from "@/lib/client";
const client = new Client(process.env.API_URL);
export default async function UserPage({ params }: { params: { id: string } }) {
const user = await client.user.getUser({ id: params.id });
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
```
### CORS Configuration
Configure CORS in your `encore.app` file:
```json
{
"id": "my-app",
"global_cors": {
"allow_origins_with_credentials": [
"http://localhost:3000",
"https://myapp.com",
"https://*.myapp.com"
]
}
}
```
### CORS Options
| Option | Description |
|--------|-------------|
| `allow_origins_without_credentials` | Origins allowed for non-credentialed requests (default: `["*"]`) |
| `allow_origins_with_credentials` | Origins allowed for credentialed requests (cookies, auth headers) |
| `allow_headers` | Additional request headers to allow |
| `expose_headers` | Additional response headers to expose |
| `debug` | Enable CORS debug logging |
### Authentication from Frontend
For authenticated requests, pass the Authorization header:
```typescript
// Using fetch
const response = await fetch("http://localhost:4000/profile", {
headers: {
"Authorization": `Bearer ${token}`,
},
});
// Or include credentials for cookie-based auth
const response = await fetch("http://localhost:4000/profile", {
credentials: "include",
});
```
With TanStack Query, configure a default fetcher:
```typescript
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryFn: async ({ queryKey }) => {
const response = await fetch(queryKey[0] as string, {
headers: { Authorization: `Bearer ${getToken()}` },
});
if (!response.ok) throw new Error("Request failed");
return response.json();
},
},
},
});
```
### Using Plain Fetch
If you prefer not to use the generated client:
```typescript
async function getUser(id: string) {
const response = await fetch(`http://localhost:4000/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
async function createUser(email: string, name: string) {
const response = await fetch("http://localhost:4000/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, name }),
});
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return response.json();
}
```
### Environment Variables
```bash
# .env.local (Next.js)
NEXT_PUBLIC_API_URL=http://localhost:4000
# .env (Vite)
VITE_API_URL=http://localhost:4000
```
### Guidelines
- Use `encore gen client` to generate typed API clients
- Regenerate the client when your API changes
- Configure CORS in `encore.app` for production domains
- Use `allow_origins_with_credentials` for authenticated requests
- Include `Authorization` header for token-based auth
- Use `credentials: "include"` for cookie-based auth
- Use environment variables for API URLs (different per environment)
- The generated client handles errors and types automatically
© encoredev · Apache-2.0 · encoredev/skills