Free tools Windows power users keep installed
One-click scans. No signup required.
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
To stream an answer based on a webpage, scrape it on your server with Firecrawl, pass the extracted content and the user’s question to a model through the Vercel AI SDK, then return a stream that the React client can render as it arrives. Keep the Firecrawl API key and scraping logic out of browser code. On the client, the current useChat API uses a transport and sendMessage; manage the input value in React state rather than expecting the hook to own it.
How the request flows
- React client: The user supplies a page URL and a question. The client sends both to your server route.
- Server route: Validate the input, scrape the page with Firecrawl, and provide the resulting content and question to your chosen model.
- Stream response: Return the model response using an AI SDK stream protocol that matches the client transport.
- React UI: Read the evolving chat messages and render their parts as they arrive.
This separates the private scraper credential from the browser and lets you choose between a simple text response and a richer message stream. Firecrawl’s Node SDK documents the client and scrape call in its Node SDK documentation; the AI SDK documents the client hook and stream formats in its useChat reference and stream protocol guide.
Build the server-side scrape and response route
Keep Firecrawl credentials on the server
Store the Firecrawl API key in a server-side environment variable such as FIRECRAWL_API_KEY. Initialize the Node SDK on the server with new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY }). Do not import that credential into a client component or return it in an API response. Firecrawl’s SDK examples use an API key to initialize the client; its Vercel Marketplace announcement describes one Vercel-specific way to provision the key into a project environment.
Recommended Free Tools
Validate inputs and scrape the page
Have the route accept a URL and a question, then validate both before making external requests. For a single-page question-answering flow, Markdown is a useful model input because it carries the page’s text without requiring the model to interpret full markup. Firecrawl’s documented call shape is:
#1 Best Overall
const result = await firecrawl.scrape(url, { formats: ['markdown'] });
Check that the scrape succeeded and that the expected Markdown content exists before continuing. Limit how much page content you send to the model, and treat scraped text as untrusted input: it may contain irrelevant instructions, misleading claims, or material that should not control your application. These are application-level safeguards for a user-facing scrape route, not guarantees provided by the scraper.
Choose a page output for the task
| Firecrawl output | Use it when |
|---|---|
| Markdown | You want readable page content to use as model context. |
| HTML | Downstream processing needs markup or DOM structure. |
| Structured JSON | You need extracted fields that fit a schema rather than an open-ended page summary. |
Firecrawl documents Markdown, HTML, and schema-guided JSON extraction in its scrape documentation. Choose based on what the next stage consumes; no output format is best for every application.
Generate and return a stream
Give the model the user’s question alongside the bounded scraped content, then return the response in the stream format your client expects. The exact generation call depends on the model provider and route framework, neither of which is fixed by this workflow. The important contract is that the server’s response format and the client’s transport agree. The AI SDK protocol guide documents both text streams and UI message data streams.
Choose the stream protocol that fits the UI
| Protocol | Best fit | Client consideration |
|---|---|---|
| Text stream | A text-only answer where chunks are concatenated into one response. | Use the corresponding text-stream transport; do not expect structured message parts. |
| UI message data stream | Richer message parts or supported data beyond a single plain-text response. | A custom backend must follow the documented UI message stream format and header, and the client must use a compatible transport. |
Choose the simpler text protocol if the interface only displays prose. If the application needs richer message parts or other supported data, use the UI message data stream and implement its protocol correctly. The AI SDK stream protocol guide explains the distinction. Transport configuration is described in the useChat API reference.
Connect the stream to React with the current useChat API
Import useChat from @ai-sdk/react. Keep the text input in ordinary React state, call sendMessage when the user submits, and render message parts from the hook’s messages as they update. The current API is transport-based and defaults to the /api/chat endpoint unless you configure a different transport. Set the endpoint and protocol to match your server route.
import { useState } from 'react';
import { useChat } from '@ai-sdk/react';
export function Chat() {
const [input, setInput] = useState('');
const { messages, sendMessage, status, stop, error } = useChat();
async function submit(event) {
event.preventDefault();
if (!input.trim()) return;
await sendMessage({ text: input });
setInput('');
}
return (
<section>
{messages.map((message) => (
<div key={message.id}>
{message.parts.map((part, index) =>
part.type === 'text'
? <p key={index}>{part.text}</p>
: null
)}
</div>
))}
<form onSubmit={submit}>
<input value={input} onChange={(event) => setInput(event.target.value)} />
<button type="submit">Ask</button>
{status === 'streaming' && <button type="button" onClick={stop}>Stop</button>}
</form>
{error && <p role="alert">The response could not be completed.</p>}
</section>
);
}
This illustrates the state and message-rendering pattern, not a complete route or a verified build. Adapt the submit payload to your server’s input contract, and use the installed SDK version’s documented types and transport settings. The current reference covers message state, parts, sending, stop behavior, errors, and transport configuration.
What to verify before deployment
- Input handling: Reject malformed or unsupported URLs, enforce sensible request and content limits, and handle scrape failures without passing an empty result to the model.
- Credential boundary: Confirm the Firecrawl key is available only to server code and is not included in client bundles or logs.
- Stream agreement: Check that the route response protocol, response headers, and client transport are aligned.
- Runtime and duration: Check the selected deployment platform’s route runtime and maximum execution duration for a scrape followed by model generation.
- Package compatibility: Pin and verify the actual React and AI SDK versions in the project. React 19 is stable, but the cited release announcement does not establish a peer-dependency matrix for a particular
@ai-sdk/reactrelease. See the React 19 announcement and current AI SDK reference.
The workflow is an implementation pattern drawn from vendor documentation, not an independently tested package combination. It does not establish compatibility for every pinned version, hosting runtime, or model provider.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Optional Vercel provisioning and scrape costs
If you deploy on Vercel, Firecrawl’s Marketplace integration is an optional way to provision a Firecrawl team and API key into the project environment; the announcement says billing is placed on the Vercel invoice. It is not a technical prerequisite for calling Firecrawl from a server route.
Best Value
Firecrawl’s tutorial published May 4, 2026 reports vendor usage figures of one credit per scrape, with JSON mode and audio extraction adding four credits each, PDF parsing adding one credit per PDF page, and ZDR adding one credit per page. The same tutorial says cached results still cost the full one credit per page. These figures are vendor-published and may change; confirm current plan terms before relying on them. Firecrawl’s scrape documentation states a default cache freshness window of 172,800,000 milliseconds (two days) and that maxAge: 0 bypasses the cache. See the Firecrawl scrape endpoint tutorial and scrape documentation.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

