# Build a client-side Word to Markdown download

Convert DOCX to Markdown client-side with JavaScript or TypeScript. Process Word files in the browser and download Markdown with images.

Source: https://docx-to-markdown.com/guides/browser

Add a Word-to-Markdown download to a browser application. A user selects a `.docx` file and receives a ZIP containing Markdown, images, and metadata. The file is converted client-side.

## Run the Client starter

[Download the Client starter](https://docx-to-markdown.com/starters/client.zip). Extract the ZIP, open its `client` directory, and use Node.js 22.12 or later:

```sh
npm install
npm run dev
```

Open the URL printed by Vite. Select a DOCX file to view Markdown, then download the Markdown, images, and metadata as a ZIP. This standalone TypeScript project uses installed packages, with no React dependency or monorepo aliases. Commit the generated lockfile to keep your package versions fixed.

## Configure font and WebAssembly assets

The starter includes this Vite configuration:

```ts
import { defineConfig } from 'vite';

export default defineConfig({
  optimizeDeps: {
    exclude: [
      '@docx-editor.dev/docx-to-markdown',
      '@docx-editor.dev/core',
      '@docx-editor.dev/fonts',
    ],
  },
  build: { assetsInlineLimit: 0 },
});
```

Excluding these packages from development pre-bundling preserves their package-relative font and WASM URLs. Vite emits the referenced assets when building for production. You do not need to copy fonts manually.

```sh
npm run build
npm run preview
```

Deploy the complete `dist` directory, including `assets`. Test a DOCX on the deployed site to check that font and WASM requests succeed. This setup targets a Vite browser application; other bundlers can require different asset handling. Use the [package documentation](https://www.docx-editor.dev/docs/2.x/export/markdown) for the conversion API.

## Download Markdown and images

Use `createMarkdownZip()` for a portable download:

```ts
import { createMarkdownZip, exportMarkdown } from '@docx-editor.dev/docx-to-markdown';

async function download(file: File) {
  const bytes = new Uint8Array(await file.arrayBuffer());
  const result = await exportMarkdown(bytes, { images: true });
  const zip = await createMarkdownZip(result);
  const blob = new Blob([zip.slice().buffer], {
    type: 'application/zip',
  });
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = 'document.zip';
  link.click();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}
```

The ZIP contains `document.md`, `document.json`, and a `media/` directory. Use relative image URLs for portable bundles; custom hosted URLs are not accepted by the ZIP helper.

## Understand network access

Browser conversion does not require uploading the document. Font resolvers can still make network requests. The current demo uses bundled substitutes and an optional Google Fonts fallback.

For an offline application, serve font and WebAssembly assets locally and ensure your font resolvers use local data. Test the fonts required by your documents before describing the application as offline.

Use `fonts` for faces that must take precedence over the bundled substitutes. Use `fallbackFonts` for additional resolvers, such as `googleFonts()`, after bundled fonts. CSS fonts on your application page do not configure the export engine. See the package's [font configuration and fallback example](https://github.com/eigenpal/docx-editor/blob/main/packages/docx-to-markdown/docs/api.md#layout-and-fonts) and inspect `result.fontResolution` to confirm which faces were used.

## Match the demo settings

The interactive demo uses HTML image dimensions and Google Fonts fallback. The starter uses bundled fonts and standard Markdown images by default. To use the demo settings, replace the options in `convert.ts`:

```ts
import { googleFonts } from '@docx-editor.dev/fonts/google';

const result = await exportMarkdown(bytes, {
  images: { syntax: 'html' },
  fallbackFonts: googleFonts(),
  resourceTimeoutMs: 30_000,
});
```

The starter already declares the fonts package. In an existing app, install it with `npm install @docx-editor.dev/fonts` before adding this import. Remote fallback requires CDN access. Matching these options does not guarantee identical pagination across package versions or font configurations.

## Connect a file input

Call `download()` when the user selects a DOCX file:

```html
<label for="document">Convert a Word document</label>
<input id="document" type="file" accept=".docx" />
<p id="status" role="status"></p>
```

```ts
const input = document.querySelector<HTMLInputElement>('#document');
const status = document.querySelector<HTMLElement>('#status');

input?.addEventListener('change', async () => {
  const file = input.files?.[0];
  if (!file || !status) return;

  input.disabled = true;
  status.textContent = 'Converting document…';
  try {
    await download(file);
    status.textContent = 'Download ready.';
  } catch {
    status.textContent = 'Could not convert this document. Try another DOCX file.';
  } finally {
    input.disabled = false;
  }
});
```

Apply a file-size limit before reading the document into memory. The file input's `accept` attribute helps with selection; the converter still needs to validate the file.

## Next steps

- [Choose image delivery for a server application](https://docx-to-markdown.com/guides/images).
- [Use a Next.js conversion endpoint](https://docx-to-markdown.com/guides/nodejs).
- [Read the package documentation](https://www.docx-editor.dev/docs/2.x/export/markdown).
