Skip to content

Remix

Use Firecrawl with Remix to scrape, search, and interact with web data in your full-stack React app.

Prerequisites

  • Remix project
  • A Firecrawl API key, get one free

Install the SDK

bash
npm install @mendable/firecrawl-js

Add your API key to .env:

bash
FIRECRAWL_API_KEY=fc-YOUR-API-KEY

Search the web

Use Firecrawl in an action to handle form submissions. Create app/routes/search.tsx:

tsx
import { json, type ActionFunctionArgs } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
import Firecrawl from "@mendable/firecrawl-js";

const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });

export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const query = formData.get("query") as string;
const results = await firecrawl.search(query, { limit: 5 });
return json({ results: (results.web || []).map((r) => ({ title: r.title, url: r.url })) });
}

export default function SearchPage() {
const data = useActionData<typeof action>();

return (
  <div>
    <Form method="post">
      <input name="query" placeholder="Search the web..." />
      <button type="submit">Search</button>
    </Form>
    {data?.results?.map((r, i) => (
      <div key={i}>
        <a href={r.url}>{r.title}</a>
      </div>
    ))}
  </div>
);
}

Scrape a page

Use Firecrawl in a loader to fetch data at request time. Create app/routes/scrape.tsx:

tsx
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import Firecrawl from "@mendable/firecrawl-js";

const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });

export async function loader({ request }: LoaderFunctionArgs) {
const url = new URL(request.url);
const target = url.searchParams.get("url");
if (!target) return json({ markdown: null });

const result = await firecrawl.scrape(target);
return json({ markdown: result.markdown });
}

export default function ScrapePage() {
const { markdown } = useLoaderData<typeof loader>();

return (
  <div>
    <h1>Scraped Content</h1>
    {markdown ? <pre>{markdown}</pre> : <p>Pass ?url= to scrape a page</p>}
  </div>
);
}

Interact with a page

Use interact to control a live browser session, click buttons, fill forms, and extract dynamic content. Create app/routes/interact.tsx:

tsx
import { json, type ActionFunctionArgs } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
import Firecrawl from "@mendable/firecrawl-js";

const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });

export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const url = formData.get("url") as string;

const result = await firecrawl.scrape(url, { formats: ['markdown'] });
const scrapeId = result.metadata?.scrapeId;

await firecrawl.interact(scrapeId, { prompt: 'Search for iPhone 16 Pro Max' });
const response = await firecrawl.interact(scrapeId, { prompt: 'Click on the first result and tell me the price' });

await firecrawl.stopInteraction(scrapeId);

return json({ output: response.output });
}

export default function InteractPage() {
const data = useActionData<typeof action>();

return (
  <div>
    <Form method="post">
      <input name="url" placeholder="URL to interact with..." />
      <button type="submit">Interact</button>
    </Form>
    {data?.output && <pre>{data.output}</pre>}
  </div>
);
}

Next steps

Scrape docs

All scrape options including formats, actions, and proxies

Search docs

Search the web and get full page content

Interact docs

Click, fill forms, and extract dynamic content

Node SDK reference

Full SDK reference with crawl, map, batch scrape, and more