npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-google-genai

v1.0.1

Published

Ergonomic React hooks for Google Gemini AI — streaming, chat, function calling, and TanStack Query integration.

Readme

✨ react-google-genai

Ergonomic React hooks for Google Gemini AI — streaming, multi-turn chat, automatic function calling, and TanStack Query integration. Pre-compiled with the React Compiler.

npm license TypeScript React TanStack Query


🎯 Overview

react-google-genai is a production-ready SDK that brings the power of Google's Gemini AI to React applications with a suite of ergonomic hooks. Built on top of TanStack Query for powerful state management, it provides:

  • 🚀 Out-of-the-box streaming — Display AI responses token-by-token for better UX
  • 💬 Multi-turn conversations — Stateful chat management with automatic history
  • Function calling — Let the AI call your custom functions automatically
  • 🎨 Flexible authentication — API Key or Vertex AI support
  • 📦 Zero-config setup — Works instantly, highly customizable
  • 🔒 Type-safe — Full TypeScript support with comprehensive JSDoc
  • ⚙️ React Compiler ready — Pre-compiled for automatic memoization

📦 Installation

npm install react-google-genai @google/genai @tanstack/react-query

Requirements

  • React 18+
  • TanStack Query 5+
  • Node.js 20+

🚀 Quick Start

Step 1: Configure your provider

Choose your authentication method:

Option A: API Key (Development)

import { GenAIProvider } from 'react-google-genai';

export function App() {
  return (
    <GenAIProvider apiKey={process.env.VITE_GEMINI_API_KEY!}>
      <YourApp />
    </GenAIProvider>
  );
}

Option B: Vertex AI (Production)

import { GenAIProvider } from 'react-google-genai';

export function App() {
  return (
    <GenAIProvider
      vertexAIConfig={{
        project: process.env.GCP_PROJECT_ID!,
        location: 'us-central1'
      }}
    >
      <YourApp />
    </GenAIProvider>
  );
}

Step 2: Use hooks in your components


📚 Core Hooks

🤖 One-Shot Generation

Generate content with a single request using useGenerateContentMutate:

import { useGenerateContentMutate } from 'react-google-genai';

export function JokeGenerator() {
  const { generate, text, isPending, error } = useGenerateContentMutate({
    model: 'gemini-2.5-flash',
    temperature: 0.9,
  });

  return (
    <div>
      <button 
        onClick={() => generate('Tell me a funny joke')} 
        disabled={isPending}
      >
        {isPending ? '⏳ Generating...' : '✨ Generate Joke'}
      </button>
      
      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
      {text && <p>{text}</p>}
    </div>
  );
}

When to use: Button clicks, form submissions, explicit user actions


🧠 Declarative Generation

Fetch generated text declaratively using useGenerateContentQuery. This hook is ideal when you want automatic caching, stale-while-revalidate behavior, and a simple query-style API.

import { useGenerateContentQuery } from 'react-google-genai';

export function QueryGenerationDemo() {
  const { data, text, isPending, isError, error, refetch } = useGenerateContentQuery({
    model: 'gemini-2.5-flash',
    prompt: 'Provide Thirukkural 619 regarding "Perseverance" and explain its relevance to modern problem solving.',
    temperature: 0.2,
    retryCount: 2,
  });

  return (
    <div>
      <button onClick={() => refetch()} disabled={isPending}>
        {isPending ? 'Fetching…' : 'Refresh'}
      </button>

      {isError && <p style={{ color: 'red' }}>Error: {error?.message}</p>}
      {text && <p>{text}</p>}
    </div>
  );
}

When to use: Data-driven pages, cache-aware requests, and reactive prompt handling


🔄 Streaming Responses

Display AI responses in real-time as they're generated with useStreamContent:

import { useStreamContent } from 'react-google-genai';

export function StreamingDemo() {
  const { stream, fullText, isStreaming, error, abort } = useStreamContent({
    model: 'gemini-2.5-flash',
    systemInstruction: 'You are a helpful coding assistant.',
  });

  return (
    <div>
      <button onClick={() => stream('Explain React hooks')} disabled={isStreaming}>
        {isStreaming ? '⏳ Streaming...' : '🎬 Start Stream'}
      </button>
      
      {isStreaming && (
        <button onClick={abort} style={{ marginLeft: '10px' }}>
          ⏹️ Stop
        </button>
      )}

      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}
      
      <div 
        style={{ 
          whiteSpace: 'pre-wrap',
          minHeight: '100px',
          padding: '10px',
          backgroundColor: '#f5f5f5',
          borderRadius: '8px'
        }}
      >
        {fullText}
        {isStreaming && '▌'}
      </div>
    </div>
  );
}

🎞️ Streaming with React Query

Use useStreamContentQuery when you want streamed AI output managed by TanStack Query. This hook returns a query result and a stable queryKey, so you can render chunked text declaratively and reuse query caching across components.

import { useStreamContentQuery } from 'react-google-genai';

export function StreamingQueryDemo() {
  const { streamQuery, queryKey } = useStreamContentQuery({
    prompt: 'Explain quantum entanglement in simple terms',
    model: 'gemini-2.5-flash',
    trigger: true,
    refetchMode: 'reset',
  });

  const fullText = streamQuery.data?.join('') ?? '';

  return (
    <div>
      <p>Query Key: {JSON.stringify(queryKey)}</p>
      {streamQuery.isFetching && <p>Streaming…</p>}
      {streamQuery.isError && <p style={{ color: 'red' }}>Error: {streamQuery.error?.message}</p>}
      <div style={{ whiteSpace: 'pre-wrap', minHeight: '120px', padding: '10px', background: '#f7f7ff', borderRadius: '8px' }}>
        {fullText}
      </div>
    </div>
  );
}

Features:

  • abort() — Cancel streaming mid-way
  • chunks — Access individual chunks for custom processing
  • fullText — Complete accumulated response
  • Real-time display of responses

⚙️ Interaction Hooks

Use the Interactions API helpers when you want structured request control or query-based interaction creation.

useInteractionBaseCreateQuery

The query variant creates an interaction declaratively and caches the result based on model + prompt.

import { useInteractionBaseCreateQuery } from 'react-google-genai';

export function InteractionQueryDemo() {
  const { interactionResponse, queryKey } = useInteractionBaseCreateQuery({
    model: 'gemini-2.5-flash',
    prompt: 'Translate this sentence into Tamil: "Hello world."',
    systemInstruction: 'You are a translator.',
    trigger: true,
  });

  const text = interactionResponse.data?.output?.[0]?.content?.[0]?.text ?? '';

  return (
    <div>
      <p>Query key: {JSON.stringify(queryKey)}</p>
      {interactionResponse.isLoading && <p>Loading…</p>}
      {interactionResponse.isError && <p style={{ color: 'red' }}>Error: {interactionResponse.error?.message}</p>}
      <p>{text}</p>
    </div>
  );
}

useInteractionBaseMutate

Use the mutation variant for imperative interaction control, including manual create and delete operations.

import { useInteractionBaseMutate } from 'react-google-genai';

export function InteractionMutateDemo() {
  const { create, delete: remove } = useInteractionBaseMutate({
    model: 'gemini-2.5-flash',
    systemInstruction: 'You are a helpful assistant.',
    onCreateError: (error) => console.error(error),
    onDeleteError: (error) => console.error(error),
  });

  const handleCreate = () => create.generate('Hello from a user');
  const handleDelete = () => remove.delete({ interactionID: 'example-id' });

  return (
    <div>
      <button onClick={handleCreate}>Create</button>
      <button onClick={handleDelete} disabled={remove.isPending}>Delete</button>
      {create.isPending && <p>Creating…</p>}
      {create.isError && <p style={{ color: 'red' }}>{create.error?.message}</p>}
      <p>{create.text}</p>
    </div>
  );
}

💬 Multi-Turn Chat

Maintain conversation history with useChat:

import { useChat } from 'react-google-genai';
import { useState } from 'react';

export function ChatInterface() {
  const [input, setInput] = useState('');
  const { sendMessage, messages, isResponding, error, reset } = useChat({
    model: 'gemini-2.5-flash',
    streaming: true, // Stream responses by default
    systemInstruction: 'You are a helpful assistant.',
  });

  const handleSend = () => {
    if (input.trim()) {
      sendMessage(input);
      setInput('');
    }
  };

  return (
    <div style={{ maxWidth: '600px' }}>
      {/* Message History */}
      <div style={{ maxHeight: '400px', overflowY: 'auto', marginBottom: '20px' }}>
        {messages.map((msg) => (
          <div 
            key={msg.id}
            style={{
              marginBottom: '10px',
              padding: '10px',
              borderRadius: '8px',
              backgroundColor: msg.role === 'user' ? '#e3f2fd' : '#f5f5f5'
            }}
          >
            <strong>{msg.role === 'user' ? '👤 You' : '🤖 Assistant'}:</strong>
            <p>{msg.text}</p>
          </div>
        ))}
        {isResponding && <p style={{ color: '#666' }}>⏳ Assistant is thinking...</p>}
      </div>

      {error && <p style={{ color: 'red' }}>Error: {error.message}</p>}

      {/* Input Area */}
      <div style={{ display: 'flex', gap: '10px' }}>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && handleSend()}
          placeholder="Type your message..."
          disabled={isResponding}
          style={{ flex: 1, padding: '10px', borderRadius: '4px', border: '1px solid #ccc' }}
        />
        <button onClick={handleSend} disabled={isResponding || !input.trim()}>
          Send
        </button>
        <button onClick={reset} style={{ backgroundColor: '#f44336', color: 'white' }}>
          Reset
        </button>
      </div>

      <p style={{ fontSize: '12px', color: '#666', marginTop: '10px' }}>
        📍 {messages.length} messages in conversation
      </p>
    </div>
  );
}

Key Features:

  • Automatic history management
  • Stateful multi-turn conversations
  • Streaming or non-streaming responses
  • Message persistence and replay

🔧 Function Calling

Let AI execute your custom functions automatically with useFunctionCalling:

import { useFunctionCalling } from 'react-google-genai';
import type { Tool } from 'react-google-genai';

// Define your tools
const tools: Tool[] = [
  {
    name: 'getWeather',
    description: 'Get current weather for a city',
    parameters: {
      type: 'OBJECT',
      properties: {
        city: { type: 'STRING', description: 'City name' },
        unit: { 
          type: 'STRING', 
          enum: ['C', 'F'],
          description: 'Temperature unit'
        }
      },
      required: ['city']
    }
  },
  {
    name: 'calculateDistance',
    description: 'Calculate distance between two cities',
    parameters: {
      type: 'OBJECT',
      properties: {
        from: { type: 'STRING', description: 'Starting city' },
        to: { type: 'STRING', description: 'Destination city' }
      },
      required: ['from', 'to']
    }
  }
];

// Implement your handlers
const handlers = {
  getWeather: async ({ city, unit = 'C' }) => {
    // Call your API or service
    const response = await fetch(`/api/weather?city=${city}&unit=${unit}`);
    return await response.json();
  },
  
  calculateDistance: async ({ from, to }) => {
    // Call your API or service
    const response = await fetch(`/api/distance?from=${from}&to=${to}`);
    return await response.json();
  }
};

export function SmartAssistant() {
  const { call, text, isPending, error, turns } = useFunctionCalling({
    model: 'gemini-2.5-flash',
    tools,
    handlers,
    systemInstruction: 'You are a travel assistant. Use the available tools to help users.',
  });

  return (
    <div>
      <button onClick={() => call('What is the weather in London and Paris?')} disabled={isPending}>
        {isPending ? '⏳ Processing...' : '🌍 Ask'}
      </button>

      {error && <p style={{ color: 'red' }}>❌ Error: {error.message}</p>}

      {text && (
        <div style={{ marginTop: '20px' }}>
          <p>{text}</p>
          <p style={{ fontSize: '12px', color: '#666' }}>
            🔄 Function calls: {turns}
          </p>
        </div>
      )}
    </div>
  );
}

How it works:

  1. User sends prompt
  2. Model analyzes and decides which functions to call
  3. SDK automatically executes matching handlers
  4. Results are sent back to model
  5. Model generates final response with function results
  6. Process repeats until model finishes

📊 Model Information

Query cached model metadata with useModelInfo:

import { useModelInfo } from 'react-google-genai';

export function ModelSelector() {
  const { data: modelInfo, isLoading, error } = useModelInfo('gemini-2.5-flash');

  if (isLoading) return <p>⏳ Loading model info...</p>;
  if (error) return <p>❌ Error: {error.message}</p>;

  return (
    <div>
      <h3>{modelInfo?.displayName}</h3>
      <p>Version: {modelInfo?.version}</p>
      <p>Input tokens: ${modelInfo?.inputTokenCostPer1M}</p>
      <p>Output tokens: ${modelInfo?.outputTokenCostPer1M}</p>
      <p>Max input: {modelInfo?.inputTokenLimit} tokens</p>
      <p>Max output: {modelInfo?.outputTokenLimit} tokens</p>
    </div>
  );
}

⚙️ Advanced Configuration

Custom QueryClient

For fine-grained control over caching and refetching:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { GenAIProvider } from 'react-google-genai';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      gcTime: 1000 * 60 * 10, // 10 minutes
      retry: 3,
    },
  },
});

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <GenAIProvider 
        apiKey={process.env.VITE_GEMINI_API_KEY!}
        queryClient={queryClient}
      >
        <YourApp />
      </GenAIProvider>
    </QueryClientProvider>
  );
}

Handling Errors

All hooks support error callbacks and error states:

const { generate, error, isPending } = useGenerateContentMutate({
  model: 'gemini-2.5-flash',
  onError: async (error) => {
    console.error('Generation failed:', error.message);
    // Log to error tracking service
    await logToSentry(error);
    // Show user-friendly message
    showNotification('Failed to generate content. Please try again.');
  }
});

// Also check error state
if (error) {
  if (error.message.includes('429')) {
    // Rate limit exceeded
  } else if (error.message.includes('401')) {
    // Invalid API key
  }
}

📖 Complete API Reference

<GenAIProvider />

Root provider component that sets up the context for all hooks.

| Prop | Type | Required | Description | |------|------|----------|-------------| | apiKey | string | ✅* | Gemini API key from ai.google.dev | | vertexAIConfig | { project: string, location: string } | ✅* | Vertex AI configuration (mutually exclusive with apiKey) | | queryClient | QueryClient | ❌ | Custom TanStack QueryClient instance | | children | ReactNode | ✅ | Child components |

* Exactly one of apiKey or vertexAIConfig is required


useGenerateContentMutate(options)

One-shot text generation with manual triggering.

Options:

interface UseGenerateContentOptions {
  model: string;                           // e.g., 'gemini-2.5-flash'
  systemInstruction?: string;              // Optional system prompt
  maxOutputTokens?: number;                // Max response length
  temperature?: number;                    // 0-2, higher = more creative
  onError?: (error: Error) => void;       // Error callback
}

Returns:

{
  generate: (prompt: string) => void;
  generateAsync: (prompt: string) => Promise<GenerateResult>;
  data: GenerateResult | null;
  text: string;                            // Extracted text from response
  isPending: boolean;
  isError: boolean;
  isSuccess: boolean;
  error: Error | null;
  status: 'idle' | 'pending' | 'success' | 'error';
  reset: () => void;
}

useStreamContent(options)

Token-by-token streaming for real-time responses.

Options: Same as useGenerateContentOptions

Returns:

{
  stream: (prompt: string) => Promise<void>;
  abort: () => void;
  reset: () => void;
  chunks: string[];
  fullText: string;
  isStreaming: boolean;
  error: Error | null;
}

useStreamContentQuery(options)

Streaming content generation integrated with TanStack Query.

Options:

interface UseStreamContentQueryOptions {
  model: string;
  prompt?: string;
  systemInstruction?: string;
  temperature?: number;
  trigger?: boolean;
  refetchMode?: 'reset' | 'merge';
  cacheConfig?: {
    staleTime?: number;
    gcTime?: number;
  };
  retryCount?: number;
}

Returns:

{
  streamQuery: {
    data?: string[];
    error?: Error;
    isFetching: boolean;
    isError: boolean;
    status: string;
    refetch: () => Promise<void>;
  };
  queryKey: readonly unknown[];
}

Note: streamQuery.data is an array of streaming chunks. Use streamQuery.data?.join('') to display the full concatenated response.


useGenerateContentQuery(options)

Declarative content generation with automatic TanStack Query caching.

Options:

interface UseGenerateContentOptionsQuery {
  model: string;
  prompt: string;
  systemInstruction?: string;
  maxOutputTokens?: number;
  temperature?: number;
  cacheConfig?: {
    staleTime?: number;
    gcTime?: number;
  };
  trigger?: boolean;
  retryCount?: number;
}

Returns:

{
  queryKey: readonly unknown[];
  data: GenerateResult | null;
  text: string;
  isPending: boolean;
  isError: boolean;
  isSuccess: boolean;
  error: Error | null;
  status: 'pending' | 'error' | 'success';
  refetch: () => Promise<void>;
}

useInteractionBaseCreateQuery(options)

Declaratively create an interaction using the Interactions API and cache the response based on model and prompt.

Options:

interface UseInteractionBaseCreateHookQuery {
  model: string;
  prompt: string;
  api_version?: string;
  systemInstruction?: string;
  temperature?: number;
  cacheConfig?: {
    staleTime?: number;
    gcTime?: number;
  };
  trigger?: boolean;
}

Returns:

{
  interactionResponse: {
    data?: Interactions.Interaction;
    error?: Error;
    isLoading: boolean;
    isError: boolean;
    status: string;
    refetch: () => Promise<void>;
  };
  queryKey: readonly unknown[];
}

useInteractionBaseMutate(options)

Imperative interaction creation and deletion with mutation-based control.

Options:

interface UseInteractionBaseCreateHookMutate {
  model: string;
  api_version?: string;
  systemInstruction?: string;
  temperature?: number;
  onCreateError?: (error: Error) => void | Promise<void>;
  onDeleteError?: (error: Error) => void | Promise<void>;
}

Returns:

{
  create: {
    generate: (prompt: string) => void;
    generateAsync: (prompt: string) => Promise<GenerateResult>;
    data: GenerateResult | null;
    text: string;
    isPending: boolean;
    isError: boolean;
    isSuccess: boolean;
    error: Error | null;
    status: 'idle' | 'pending' | 'success' | 'error';
    reset: () => void;
  };
  delete: {
    delete: (variables: { interactionID: string; interactionDeleteParams?: Interactions.InteractionDeleteParams }) => void;
    deleteAsync: (variables: { interactionID: string; interactionDeleteParams?: Interactions.InteractionDeleteParams }) => Promise<unknown>;
    data: unknown | null;
    variables: unknown;
    isPending: boolean;
    isError: boolean;
    isSuccess: boolean;
    error: Error | null;
    status: 'idle' | 'pending' | 'success' | 'error';
    reset: () => void;
  };
}

useChat(options)

Multi-turn conversation management.

Options:

interface UseChatOptions {
  model: string;
  systemInstruction?: string;
  temperature?: number;
  streaming?: boolean;                     // Default: true
}

Returns:

{
  sendMessage: (message: string) => void;
  sendMessageAsync: (message: string) => Promise<void>;
  messages: ChatMessage[];
  isResponding: boolean;
  error: Error | null;
  reset: () => void;
  messageCount: number;
}

useFunctionCalling(options)

Automatic function invocation by the model.

Options:

interface UseFunctionCallingOptions {
  model: string;
  tools: Tool[];
  handlers: Record<string, (args: Record<string, unknown>) => Promise<unknown> | unknown>;
  systemInstruction?: string;
  temperature?: number;
}

Returns:

{
  call: (prompt: string) => void;
  callAsync: (prompt: string) => Promise<GenerateResult>;
  data: GenerateResult | null;
  text: string;
  turns: number;                           // Number of function calls
  isPending: boolean;
  isError: boolean;
  error: Error | null;
  reset: () => void;
}

useModelInfo(model: string)

Query model metadata and capabilities.

Returns: Standard TanStack Query result

{
  data: Model | undefined;
  isLoading: boolean;
  isError: boolean;
  error: Error | null;
  refetch: () => Promise<void>;
}

🎨 Best Practices

1. Authentication

// ✅ Load API key from environment
const apiKey = process.env.VITE_GEMINI_API_KEY;
if (!apiKey) throw new Error('Missing VITE_GEMINI_API_KEY');

<GenAIProvider apiKey={apiKey}>
  <App />
</GenAIProvider>

// ❌ Never hardcode API keys
// ❌ Never expose API keys in client-side code in production

2. Temperature Selection

// Deterministic (Q&A, extraction)
temperature: 0.1

// Balanced (general use)
temperature: 0.7

// Creative (brainstorming, writing)
temperature: 1.5

3. Error Handling

const { generate, error, isPending } = useGenerateContentMutate({
  model: 'gemini-2.5-flash',
  onError: handleError, // Always provide error handler
});

function handleError(error: Error) {
  if (error.message.includes('429')) {
    // Implement exponential backoff
  } else if (error.message.includes('401')) {
    // Refresh authentication
  }
}

4. Streaming for Better UX

// Good: Stream long responses
<useStreamContent model="gemini-2.5-flash" />

// Also good: Non-streaming for quick responses
<useGenerateContentMutate model="gemini-2.5-flash" />

5. System Instructions

// Specific instructions improve output quality
systemInstruction: `
You are an expert React developer.
- Always use TypeScript
- Provide production-ready code
- Include error handling
- Add descriptive comments
`

🔍 Troubleshooting

API Key Issues

Error: Invalid API key
→ Verify key at https://ai.google.dev
→ Check environment variable is loaded
→ Ensure no extra whitespace

Rate Limiting

Error: 429 Too Many Requests
→ Implement exponential backoff
→ Use streaming to reduce token usage
→ Check your quota at Google Cloud Console

CORS Errors

Error: CORS policy blocked request
→ Use Vertex AI for backend integration
→ Implement backend proxy
→ Check allowed origins in API settings

Model Not Found

Error: Model 'xyz' not found
→ Use 'gemini-2.5-flash' or 'gemini-1.5-pro'
→ Check model availability in your region
→ Verify model name spelling

📊 Performance Tips

  1. Memoize handlers in function calling:

    const handlers = useMemo(() => ({ getWeather, getLocation }), []);
  2. Use streaming for long responses:

    • Improves perceived performance
    • Better user experience
    • Reduced memory usage
  3. Cache model info:

    • Queries are automatically cached
    • Use custom QueryClient for fine-tuning
  4. Batch function calls:

    • Group related functions together
    • Reduces model latency
  5. React Compiler benefits:

    • Pre-compiled for automatic memoization
    • No additional setup needed
    • Works even without React Compiler enabled

🧪 Testing

import { renderHook, act } from '@testing-library/react';
import { useGenerateContentMutate } from 'react-google-genai';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

it('generates content', async () => {
  const wrapper = ({ children }) => (
    <QueryClientProvider client={new QueryClient()}>
      {children}
    </QueryClientProvider>
  );

  const { result } = renderHook(() => useGenerateContentMutate({
    model: 'gemini-2.5-flash'
  }), { wrapper });

  act(() => {
    result.current.generate('Hello');
  });

  expect(result.current.isPending).toBe(true);
});

📚 Resources


🤝 Contributing

We welcome contributions! Please feel free to submit issues and pull requests.


📄 License

MIT © 2026 @sanjaiyan-dev