Bỏ qua để vào nội dung chính
Build an AI-Powered Autocomplete Form With the Vercel AI SDK

Build an AI-Powered Autocomplete Form With the Vercel AI SDK

Bởi Lucas Fischer
25 thg 3, 20266 phút đọc

Add intelligent autocomplete to your forms using streaming AI suggestions. Learn to build a context-aware autocomplete that understands what users are trying to fill in.

Beyond Simple Autocomplete

Traditional autocomplete matches strings from a preset list. AI autocomplete understands context — it can suggest a professional bio based on someone's job title, complete an address from partial input, or finish a product description mid-sentence. Here's how to build it with the Vercel AI SDK.

Setting Up the API Route

// app/api/autocomplete/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

export async function POST(request: Request) {
  const { field, value, context } = await request.json();

  const result = streamText({
    model: openai('gpt-4o-mini'), // fast and cheap for autocomplete
    system: `You are an autocomplete assistant for a professional form.
    Complete the user's input naturally and professionally.
    Return ONLY the completion text — not what the user already typed.
    Maximum 50 words. No explanations.`,
    prompt: `Field: "${field}"
Context: ${JSON.stringify(context)}
User typed: "${value}"
Complete this:`,
    maxTokens: 100,
  });

  return result.toTextStreamResponse();
}

The Autocomplete Hook

// hooks/useAIAutocomplete.ts
import { useState, useCallback, useRef } from 'react';

export function useAIAutocomplete(field: string, context: Record<string, string>) {
  const [suggestion, setSuggestion] = useState('');
  const [loading, setLoading] = useState(false);
  const abortRef = useRef<AbortController | null>(null);

  const getSuggestion = useCallback(async (value: string) => {
    if (value.length < 10) { setSuggestion(''); return; }
    
    // Cancel previous request
    abortRef.current?.abort();
    abortRef.current = new AbortController();
    
    setLoading(true);
    setSuggestion('');
    
    try {
      const response = await fetch('/api/autocomplete', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ field, value, context }),
        signal: abortRef.current.signal,
      });

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

      while (true) {
        const { done, value: chunk } = await reader.read();
        if (done) break;
        accumulated += decoder.decode(chunk);
        setSuggestion(accumulated);
      }
    } catch (e) {
      if ((e as Error).name !== 'AbortError') console.error(e);
    } finally {
      setLoading(false);
    }
  }, [field, context]);

  return { suggestion, loading, getSuggestion };
}

The Autocomplete Input Component

'use client';
import { useDebounce } from 'use-debounce';
import { useAIAutocomplete } from '@/hooks/useAIAutocomplete';

interface Props {
  name: string;
  label: string;
  formContext: Record<string, string>;
  value: string;
  onChange: (value: string) => void;
}

export function AITextField({ name, label, formContext, value, onChange }: Props) {
  const { suggestion, getSuggestion } = useAIAutocomplete(name, formContext);
  const [debouncedGet] = useDebounce(getSuggestion, 600);

  function handleKeyDown(e: React.KeyboardEvent) {
    // Tab accepts the suggestion
    if (e.key === 'Tab' && suggestion) {
      e.preventDefault();
      onChange(value + suggestion);
    }
  }

  return (
    <div className="relative">
      <label className="block text-sm font-medium mb-1">{label}</label>
      <div className="relative">
        <input
          value={value}
          onChange={e => {
            onChange(e.target.value);
            debouncedGet(e.target.value);
          }}
          onKeyDown={handleKeyDown}
          className="w-full p-3 border rounded-lg"
        />
        {suggestion && (
          <span className="absolute inset-0 p-3 pointer-events-none text-gray-400">
            <span className="invisible">{value}</span>
            {suggestion}
          </span>
        )}
      </div>
      {suggestion && (
        <p className="text-xs text-gray-400 mt-1">Press Tab to accept suggestion</p>
      )}
    </div>
  );
}

Practical Use Cases

  • Professional bio fields that suggest based on job title
  • Product descriptions that mirror existing products' style
  • Email templates that complete based on subject and recipient type
  • Address forms that infer city/state from zip code context

The key to great AI autocomplete is good context: pass relevant already-filled fields so the model understands what the user is building. With gpt-4o-mini, each autocomplete costs roughly $0.0001 — essentially free at any realistic usage volume.

Không spam, hủy đăng ký bất kỳ lúc nào.

Bài viết liên quan