useChat in v7 takes a transport object, not api/headers options, and it no longer manages your input field. Old tutorials will mislead you on both.

'use client';

import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { useState } from 'react';

export default function Page() {
  const { messages, sendMessage, status, error, stop } = useChat({
    transport: new DefaultChatTransport({ api: '/api/chat' }),
  });
  const [input, setInput] = useState('');

  // Render structure (JSX):
//   fragment
//     messages.map(message =>
//       [div keyed by message id]
//         role label: message.role === 'user' ? 'User: ' : 'AI: '
//         message.parts.map((part, i) =>
//           part.type === 'text' ? [span keyed by i] holding part.text : null))
//     [form onSubmit]: e.preventDefault(); sendMessage({ text: input }); setInput('')
//       [input]: value={input}, onChange={e => setInput(e.target.value)} sendMessage({ text: input }); setInput(''); }}>
        [input value={input} onChange={e =] setInput(e.target.value)} />
      [/form]
    [/]
  );
}

Checklist:
1. Wrap options in new DefaultChatTransport({ api: '/api/chat' }). Passing api/headers/credentials directly to useChat was removed in v5.
2. Manage input with your own useState. The old input, handleInputChange, handleSubmit return values are gone.
3. Send with sendMessage({ text: input }), not handleSubmit. Request extras go in the second arg: sendMessage({ text }, { body: { custom: 'value' } }).
4. Render message.parts, not message.content. Text lives in parts with type 'text'. Tool calls live in tool parts on the same array.
5. status tells you 'submitted' | 'streaming' | 'ready' | 'error'. Use it for spinners and to disable the form while streaming.
6. The old data and allowEmptySubmit request options were removed in v5. Move custom data into body.