React Integration
Mearie provides React hooks for queries, mutations, fragments, and subscriptions with full type safety and React Suspense support.
Installation
Install the core package and the React integration:
npm install -D mearie
npm install @mearie/reactyarn add -D mearie
yarn add @mearie/reactpnpm add -D mearie
pnpm add @mearie/reactbun add -D mearie
bun add @mearie/reactdeno add --dev npm:mearie
deno add npm:@mearie/reactSetup
1. Add Build Plugin
Add Mearie's build plugin to enable automatic type generation from your GraphQL documents:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import mearie from 'mearie/vite';
export default defineConfig({
plugins: [react(), mearie()],
});// next.config.js
import withMearie from 'mearie/next';
export default withMearie({
// Your Next.js config
});TIP
By default, Mearie looks for ./schema.graphql relative to your vite.config.ts or next.config.js. For custom schema locations or advanced configuration, see Codegen Config.
2. Create Client
Create a GraphQL client with your API endpoint. Import createClient and exchanges from @mearie/react:
// src/lib/graphql-client.ts
import { createClient, httpExchange, cacheExchange, dedupExchange } from '@mearie/react';
import { schema } from '$mearie';
export const client = createClient({
schema,
exchanges: [
dedupExchange(),
cacheExchange(),
httpExchange({
url: 'https://api.example.com/graphql',
}),
],
});See Exchanges for more details on available exchanges and middleware.
3. Set Up Provider
Wrap your app with the client provider to make the GraphQL client available throughout your component tree:
// src/app.tsx
import { ClientProvider } from '@mearie/react';
import { client } from './lib/graphql-client';
<ClientProvider client={client}>
<App />
</ClientProvider>;Hooks
useQuery
Fetch data with automatic caching and updates:
import { graphql } from '$mearie';
import { useQuery } from '@mearie/react';
export const UserProfile = ({ userId }: { userId: string }) => {
const { data, loading, error, refetch } = useQuery(
graphql(`
query GetUserQuery($id: ID!) {
user(id: $id) {
id
name
email
avatar
bio
age
}
}
`),
{
id: userId,
},
);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<img src={data.user.avatar} alt={data.user.name} />
<h1>{data.user.name}</h1>
{data.user.bio && <p>{data.user.bio}</p>}
<p>Email: {data.user.email}</p>
<p>Age: {data.user.age}</p>
<button onClick={() => refetch()}>Refresh</button>
</div>
);
};useMutation
Modify data with automatic cache updates:
import { useState } from 'react';
import { graphql } from '$mearie';
import { useMutation } from '@mearie/react';
export const EditUserForm = ({ userId }: { userId: string }) => {
const [name, setName] = useState('');
const [updateUser, { loading }] = useMutation(
graphql(`
mutation UpdateUserMutation($id: ID!, $name: String!) {
updateUser(id: $id, input: { name: $name }) {
id
name
}
}
`),
);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
await updateUser({ id: userId, name });
};
return (
<form onSubmit={handleSubmit}>
<input value={name} onChange={(e) => setName(e.target.value)} required />
<button type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Save'}
</button>
</form>
);
};useFragment
Co-locate data requirements with components:
import { graphql } from '$mearie';
import { useFragment } from '@mearie/react';
import type { UserCard_user$key } from '$mearie';
export const UserCard = ({ user }: { user: UserCard_user$key }) => {
const fragment = useFragment(
graphql(`
fragment UserCard_user on User {
id
name
avatar
email
}
`),
user,
);
return (
<div className="card">
<img src={fragment.data.avatar} alt={fragment.data.name} />
<h3>{fragment.data.name}</h3>
<p>{fragment.data.email}</p>
</div>
);
};useSubscription
Real-time updates via subscriptions:
import { graphql } from '$mearie';
import { useSubscription } from '@mearie/react';
interface ChatMessagesProps {
chatId: string;
}
export const ChatMessages = ({ chatId }: ChatMessagesProps) => {
const { data, loading } = useSubscription(
graphql(`
subscription MessageAddedSubscription($chatId: ID!) {
messageAdded(chatId: $chatId) {
id
body
author {
name
}
}
}
`),
{
chatId,
},
);
return (
<div>
<div>{loading ? 'Connecting...' : 'Connected'}</div>
{data?.messageAdded && (
<div>
<strong>{data.messageAdded.author.name}:</strong>
{data.messageAdded.body}
</div>
)}
</div>
);
};React Suspense
Use with React Suspense for simpler loading states:
import { Suspense } from 'react';
import { graphql } from '$mearie';
import { useQuery } from '@mearie/react';
const UserProfile = ({ userId }: { userId: string }) => {
const { data } = useQuery(
graphql(`
query GetUserQuery($id: ID!) {
user(id: $id) {
id
name
email
}
}
`),
{ id: userId },
{ suspense: true },
);
return <h1>{data.user.name}</h1>;
};
const App = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile userId="123" />
</Suspense>
);
};Next Steps
- Queries - Learn more about queries
- Mutations - Learn more about mutations
- Fragments - Learn more about fragments
- Subscriptions - Learn more about subscriptions