DEV Community

Apollo
Apollo

Posted on

How to integrate DeepSeek R1 into your React app

Integrating DeepSeek R1 into Your React Application: A Technical Deep Dive

DeepSeek R1 is a powerful open-source large language model that offers state-of-the-art performance while being optimized for efficiency. This guide will walk you through the complete process of integrating DeepSeek R1 into a React application, covering everything from basic setup to advanced streaming implementations.

Prerequisites

Before we begin, ensure you have:

  • Node.js (v18+ recommended)
  • React (v18+)
  • Basic understanding of async/await and React hooks
  • API key from DeepSeek (if using their hosted service)

Step 1: Setting Up the Project

First, create a new React application if you don't have one already:

npx create-react-app deepseek-integration
cd deepseek-integration
Enter fullscreen mode Exit fullscreen mode

Install the required dependencies:

npm install @deepseek/sdk axios event-source-parser
Enter fullscreen mode Exit fullscreen mode

Step 2: Creating the DeepSeek Service Layer

We'll create a dedicated service module to handle all DeepSeek API interactions. Create src/services/deepseek.js:

import axios from 'axios';
import { SSE } from 'event-source-parser';

const DEEPSEEK_API_URL = 'https://api.deepseek.com/v1';
const DEFAULT_MODEL = 'deepseek-r1';

export class DeepSeekService {
  constructor(apiKey) {
    this.client = axios.create({
      baseURL: DEEPSEEK_API_URL,
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json'
      }
    });
  }

  async complete(prompt, options = {}) {
    const response = await this.client.post('/chat/completions', {
      model: DEFAULT_MODEL,
      messages: [{ role: 'user', content: prompt }],
      temperature: options.temperature || 0.7,
      max_tokens: options.max_tokens || 2048,
      ...options
    });
    return response.data;
  }

  async stream(prompt, options = {}, onData) {
    const response = await fetch(`${DEEPSEEK_API_URL}/chat/completions`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
        'Accept': 'text/event-stream'
      },
      body: JSON.stringify({
        model: DEFAULT_MODEL,
        messages: [{ role: 'user', content: prompt }],
        stream: true,
        ...options
      })
    });

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });

      // Process each complete event
      const events = buffer.split('\n\n');
      buffer = events.pop();

      for (const event of events) {
        if (event.includes('[DONE]')) continue;

        try {
          const data = JSON.parse(event.replace('data: ', ''));
          if (data.choices?.[0]?.delta?.content) {
            onData(data.choices[0].delta.content);
          }
        } catch (e) {
          console.error('Error parsing event:', e);
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Creating a Custom Hook for DeepSeek

Let's create a React hook to manage the DeepSeek integration state. Create src/hooks/useDeepSeek.js:

import { useState, useCallback, useRef } from 'react';
import { DeepSeekService } from '../services/deepseek';

export function useDeepSeek(apiKey) {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [response, setResponse] = useState('');
  const serviceRef = useRef(new DeepSeekService(apiKey));

  const reset = useCallback(() => {
    setResponse('');
    setError(null);
  }, []);

  const complete = useCallback(async (prompt, options) => {
    setIsLoading(true);
    setError(null);

    try {
      const result = await serviceRef.current.complete(prompt, options);
      setResponse(result.choices[0].message.content);
    } catch (err) {
      setError(err.message || 'Failed to get response from DeepSeek');
    } finally {
      setIsLoading(false);
    }
  }, []);

  const stream = useCallback(async (prompt, options) => {
    setIsLoading(true);
    setError(null);
    setResponse('');

    try {
      await serviceRef.current.stream(prompt, options, (chunk) => {
        setResponse(prev => prev + chunk);
      });
    } catch (err) {
      setError(err.message || 'Failed to stream response from DeepSeek');
    } finally {
      setIsLoading(false);
    }
  }, []);

  return {
    isLoading,
    error,
    response,
    complete,
    stream,
    reset
  };
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Building the UI Component

Now let's create a component that uses our hook. Create src/components/DeepSeekChat.js:

import { useState } from 'react';
import { useDeepSeek } from '../hooks/useDeepSeek';

export function DeepSeekChat({ apiKey }) {
  const [prompt, setPrompt] = useState('');
  const [useStreaming, setUseStreaming] = useState(true);
  const { isLoading, error, response, complete, stream, reset } = useDeepSeek(apiKey);

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!prompt.trim()) return;

    if (useStreaming) {
      await stream(prompt, {
        temperature: 0.7,
        max_tokens: 2048
      });
    } else {
      await complete(prompt, {
        temperature: 0.7,
        max_tokens: 2048
      });
    }
  };

  return (
    <div className="deepseek-chat">
      <form onSubmit={handleSubmit}>
        <textarea
          value={prompt}
          onChange={(e) => setPrompt(e.target.value)}
          placeholder="Enter your prompt..."
          disabled={isLoading}
        />
        <div className="controls">
          <label>
            <input
              type="checkbox"
              checked={useStreaming}
              onChange={() => setUseStreaming(!useStreaming)}
            />
            Use Streaming
          </label>
          <button type="submit" disabled={isLoading}>
            {isLoading ? 'Processing...' : 'Submit'}
          </button>
          <button type="button" onClick={reset} disabled={isLoading}>
            Reset
          </button>
        </div>
      </form>

      {error && <div className="error">{error}</div>}

      {response && (
        <div className="response">
          <h3>Response:</h3>
          <div>{response}</div>
        </div>
      )}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Optimizing Performance with Memoization

For better performance with frequent updates during streaming, we can optimize our component:

import { memo, useState } from 'react';
import { useDeepSeek } from '../hooks/useDeepSeek';

const ResponseDisplay = memo(({ text }) => {
  return <div className="response-content">{text}</div>;
});

export const DeepSeekChat = memo(({ apiKey }) => {
  // ... rest of the component code remains the same

  return (
    // ... other JSX
    {response && (
      <div className="response">
        <h3>Response:</h3>
        <ResponseDisplay text={response} />
      </div>
    )}
  );
});
Enter fullscreen mode Exit fullscreen mode

Advanced: Implementing Conversation History

To maintain conversation context, modify the hook to track message history:

export function useDeepSeek(apiKey) {
  const [conversation, setConversation] = useState([]);
  // ... other state

  const addToConversation = useCallback((role, content) => {
    setConversation(prev => [...prev, { role, content }]);
  }, []);

  const complete = useCallback(async (prompt, options) => {
    setIsLoading(true);
    setError(null);
    addToConversation('user', prompt);

    try {
      const result = await serviceRef.current.complete(prompt, {
        ...options,
        messages: conversation.concat({ role: 'user', content: prompt })
      });

      const assistantMessage = result.choices[0].message.content;
      addToConversation('assistant', assistantMessage);
      setResponse(assistantMessage);
    } catch (err) {
      setError(err.message || 'Failed to get response from DeepSeek');
    } finally {
      setIsLoading(false);
    }
  }, [conversation, addToConversation]);

  // Similar modifications for stream method

  return {
    conversation,
    // ... other returns
  };
}
Enter fullscreen mode Exit fullscreen mode

Error Handling and Rate Limiting

Implement robust error handling and rate limiting:

// In the DeepSeekService class
async complete(prompt, options = {}) {
  try {
    const response = await this.client.post('/chat/completions', {
      // ... request body
    });

    if (response.status === 429) {
      const retryAfter = response.headers['retry-after'] || 1;
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      return this.complete(prompt, options);
    }

    return response.data;
  } catch (error) {
    if (error.response) {
      throw new Error(`DeepSeek API Error: ${error.response.data.error?.message || error.message}`);
    }
    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

Testing the Integration

Create a simple test page in src/App.js:

import { DeepSeekChat } from './components/DeepSeekChat';

function App() {
  return (
    <div className="App">
      <h1>DeepSeek R1 Integration</h1>
      <DeepSeekChat apiKey={process.env.REACT_APP_DEEPSEEK_API_KEY} />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Deployment Considerations

When deploying to production:

  1. Never expose your API key in client-side code (use environment variables)
  2. Consider implementing a backend proxy for additional security
  3. Implement proper CORS policies
  4. Add rate limiting on your frontend

Conclusion

This implementation provides a robust integration of DeepSeek R1 into your React application, featuring both regular and streaming responses, conversation history, and comprehensive error handling. The modular architecture allows for easy extension and customization to fit your specific requirements.

For production applications, consider adding:

  • Token usage tracking
  • Response caching
  • User authentication
  • More sophisticated conversation management

Remember to always monitor your API usage and optimize your prompts for best results with DeepSeek R1's capabilities.


🚀 Stop Writing Boilerplate Prompts

If you want to skip the setup and code 10x faster with complete AI architecture patterns, grab my Senior React Developer AI Cookbook ($19). It includes Server Action prompt libraries, UI component generation loops, and hydration debugging strategies.

Browse all 10+ developer products at the Apollo AI Store | Or snipe Solana tokens free via @ApolloSniper_Bot.

Top comments (0)