Skip to main content

What you will build

A reusable React component that embeds the assistant directly in your application. The component provides:
  • A floating widget that opens a chat panel when clicked
  • Real-time streaming responses based on information from your documentation
Users can use the widget to get help with your product without leaving your application. Clone the repository to start working with a complete example locally.

Prerequisites

  • Mintlify Pro or Custom plan
  • Your domain name, which appears at the end of your dashboard URL. For example, if your dashboard URL is https://dashboard.mintlify.com/org-name/domain-name, your domain name is domain-name
  • An assistant API key
  • Node.js v18 or higher and npm installed
  • Basic React knowledge
  • Existing React application (Next.js, Vite, Create React App, or similar). This guide uses a Vite application

Get your assistant API key

  1. Navigate to the API keys page in your dashboard.
  2. Click Create Assistant API Key.
  3. Copy the assistant API key (starts with mint_dsc_) and save it securely.
The assistant API key is a public token that can be used in frontend code. Calls using this token count toward your plan’s message allowance and can incur overages.

Build the widget

1

Install dependencies

Install the AI SDK for React and a Markdown renderer:
npm install ai @ai-sdk/react react-markdown
2

Create the assistant component

Create a new file AssistantWidget.jsx (or .tsx if using TypeScript) in your components directory:
AssistantWidget.jsx
import { useChat } from '@ai-sdk/react';
import { useState } from 'react';
import ReactMarkdown from 'react-markdown';

export function AssistantWidget({ domain }) {
  const [isOpen, setIsOpen] = useState(false);

  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: `https://api-dsc.mintlify.com/v1/assistant/${domain}/message`,
    headers: {
      'Authorization': `Bearer ${process.env.VITE_MINTLIFY_TOKEN}`,
    },
    body: {
      fp: 'anonymous',
      retrievalPageSize: 5,
    },
    streamProtocol: 'data',
    sendExtraMessageFields: true,
  });

  return (
    <>
      <button
        onClick={() => setIsOpen(!isOpen)}
        className="fixed bottom-4 right-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
      >
        {isOpen ? 'Close' : 'Ask'}
      </button>

      {isOpen && (
        <div className="fixed bottom-16 right-4 w-96 h-[500px] bg-white rounded-lg shadow-xl flex flex-col z-50 border border-gray-200">
          <div className="p-4 border-b border-gray-200 bg-gray-50 rounded-t-lg">
            <h3 className="font-semibold text-gray-900">Documentation assistant</h3>
          </div>

          <div className="flex-1 overflow-y-auto p-4 space-y-4">
            {messages.map((message) => (
              <div key={message.id} className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}>
                <div className={`max-w-[75%] rounded-lg px-4 py-2 ${message.role === 'user' ? 'bg-blue-600 text-white' : 'bg-gray-100 text-gray-900'}`}>
                  {message.role === 'user' ? (
                    <div className="text-sm">{message.content}</div>
                  ) : (
                    <ReactMarkdown className="prose prose-sm max-w-none text-sm">{message.content}</ReactMarkdown>
                  )}
                </div>
              </div>
            ))}

            {isLoading && <div className="text-gray-500 text-sm">Loading...</div>}
          </div>

          <form onSubmit={handleSubmit} className="p-4 border-t border-gray-200 bg-white rounded-b-lg">
            <div className="flex gap-2">
              <input
                type="text"
                value={input}
                onChange={handleInputChange}
                placeholder="Ask a question..."
                className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                disabled={isLoading}
              />
              <button
                type="submit"
                disabled={isLoading || !input.trim()}
                className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400 text-sm"
              >
                Send
              </button>
            </div>
          </form>
        </div>
      )}
    </>
  );
}
3

Configure environment variables

Add your assistant API token to your .env file in your project root:
.env
VITE_MINTLIFY_TOKEN=mint_dsc_your_token_here
Restart your development server to apply the changes.
Vite requires environment variables to be prefixed with VITE_. Use the appropriate prefix for your framework.
4

Add the widget to your app

Import and render the component in your application. Pass your Mintlify project domain (the end of your dashboard URL. For example, domain-name from https://dashboard.mintlify.com/org-name/domain-name).
App.jsx
import { AssistantWidget } from './components/AssistantWidget';

function App() {
  return (
    <div>
      {/* Your existing app content */}
      <AssistantWidget domain="yourcompany" />
    </div>
  );
}

export default App;
5

Test the widget

  1. Start your development server:
npm run dev
  1. Open your application in a browser.
  2. Click the “Ask” button in the bottom-right corner.
  3. Ask a question about your documentation.

Customization ideas

This example shows a simple implementation of the assistant API. Here are some ways to begin customizing the widget.

Add color customization

Accept color props and use them for theming:
export function AssistantWidget({ domain, buttonColor = '#2563eb' }) {
  // ... rest of component
  return (
    <>
      <button
        style={{ backgroundColor: buttonColor }}
        // ... rest of button
      >

Add widget positioning options

Let users position the widget in different corners:
export function AssistantWidget({ domain, position = 'bottom-right' }) {
  const positions = {
    'bottom-right': 'bottom-4 right-4',
    'bottom-left': 'bottom-4 left-4',
    'top-right': 'top-4 right-4',
    'top-left': 'top-4 left-4',
  };

  return (
    <>
      <button className={`fixed ${positions[position]} ...`}

Source citations

Extract and display sources from assistant responses:
const extractSources = (parts) => {
  return parts
    ?.filter(p => p.type === 'tool-invocation' && p.toolInvocation?.toolName === 'search')
    .flatMap(p => p.toolInvocation?.result || [])
    .map(source => ({
      url: source.url || source.path,
      title: source.metadata?.title || source.path,
    })) || [];
};

// In your message rendering:
{messages.map((message) => {
  const sources = message.role === 'assistant' ? extractSources(message.parts) : [];
  return (
    <div key={message.id}>
      {/* message content */}
      {sources.length > 0 && (
        <div className="mt-2 text-xs">
          <p className="font-semibold">Sources:</p>
          {sources.map((s, i) => (
            <a key={i} href={s.url} target="_blank" className="text-blue-600">
              {s.title}
            </a>
          ))}
        </div>
      )}
    </div>
  );
})}

Troubleshooting

  • Verify the component is imported and rendered in your app.
  • Check that Tailwind CSS or your styling solution is properly configured.
  • Ensure the z-index values do not conflict with other fixed elements.
  • Verify your API token starts with mint_dsc_.
  • Check the token is correctly set in your environment variables.
  • Confirm you are using the public assistant API token, not the admin API key.
  • Restart your development server after adding environment variables.
  • Verify your project domain is correct.
  • Check that your Mintlify plan includes the assistant feature.
  • Confirm your documentation site is published and accessible.
  • Verify the assistant API token is correctly set in your environment variables.
Open developer tools and check the Network tab to troubleshoot:
  • Look for 401 or 403 authentication errors
  • Verify API requests are reaching https://api-dsc.mintlify.com/v1/assistant/{domain}/message
  • Check the response status and error messages