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 mearie @mearie/react
yarn add mearie @mearie/react
pnpm add mearie @mearie/react
bun add mearie @mearie/react
deno add npm:mearie npm:@mearie/react
Setup
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. Links are middleware-style handlers that process requests and responses. At least one terminating link is required (in this case, httpLink
). See Links for more details.
// src/lib/graphql-client.ts
import { createClient, httpLink, cacheLink, dedupLink } from 'mearie';
export const client = createClient({
links: [
dedupLink(),
cacheLink(),
httpLink({
url: 'https://api.example.com/graphql',
}),
],
});
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/types';
export const UserCard = ({ user }: { user: UserCard_user$key }) => {
const data = useFragment(
graphql(`
fragment UserCard_user on User {
id
name
avatar
email
}
`),
user,
);
return (
<div className="card">
<img src={data.avatar} alt={data.name} />
<h3>{data.name}</h3>
<p>{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