> ## Documentation Index
> Fetch the complete documentation index at: https://docs.markpdf.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Framework guide

> Where the API key lives, when to use direct @markpdf/react and when to proxy with @markpdf/nextjs.

# Framework guide

## The API key in the browser

`MarkpdfProvider` initializes a `MarkpdfClient` **on the client**, so the API key you pass to it ends up in the JS bundle that is downloaded to the browser. Anyone can open the developer tools, go to the Network tab or inspect the bundle, and copy it.

This is acceptable when:

* You are prototyping or building an internal tool with controlled access.
* You already have a low privilege key, with adjusted usage limits, dedicated only to that frontend.
* The "cost" of someone using your key to convert PDFs is not a real concern (e.g. single-user tool, demo, staging environment).

It is not acceptable for a public production app with a key that also protects billing or shared limits.

<Warning>
  There is no way to "hide" the key in a Client Component with obfuscation alone — the browser needs the actual value in plain text to send it in the `x-api-key` header. The only real mitigation is to not expose it: move the call to API to the server.
</Warning>

## Recommended pattern for production: proxy in your own backend

If your app has a backend (Next.js, Express, any framework with server-side routes), do not initialize `MarkpdfClient`/`MarkpdfProvider` on the client. Instead:

1. Upload the file to your own endpoint (`/api/convert` or similar).
2. Your server calls markpdf with the key saved as a server-side environment variable.
3. Your server returns the Markdown to the client.

If your app is in Next.js, `@markpdf/nextjs` already implements this pattern — see [Installing Next.js](/docs/sdks/nextjs/installation) and [Next.js Framework Guide](/docs/sdks/nextjs/framework-guide) (Route Handlers and Server Actions). The React component that uploads the file can still look like the one in `useConvertFile`, but pointing to your own path:

```tsx theme={null}
async function upload(file: File) {
  const form = new FormData();
  form.append("file", file);
  const res = await fetch("/api/convert", { method: "POST", body: form });
  return res.text();
}
```

With other frameworks (Express, Fastify, etc.), the same principle applies: deploy your own endpoint with `@markpdf/sdk` on the server, and consume that endpoint from React with normal `fetch` instead of `@markpdf/react`.

## When to use `@markpdf/react` direct

* Apps without their own backend (SPA static served from a CDN) where you have nowhere to hide any keys anyway.
* Internal tools behind VPN/SSO where the public who can see the key is already authorized to use it.
* Prototipos y demos.

## Server-Side Rendering (SSR) / Server Components

`MarkpdfProvider` uses `useMemo`, `useContext` and creates the client at render time — it is a Client Component. Check it explicitly if your framework requires it:

```tsx providers.tsx theme={null}
"use client";

import { MarkpdfProvider } from "@markpdf/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return <MarkpdfProvider apiKey={process.env.NEXT_PUBLIC_MARKPDF_API_KEY!}>{children}</MarkpdfProvider>;
}
```

<Note>
  If you need to mark the key as `NEXT_PUBLIC_*` (or your framework's equivalent public prefix) so that `MarkpdfProvider` receives it, you've already assumed the trade-off of exposing it to the browser. Review the previous section before doing it in production.
</Note>
