Import/Export
Move files out of the page and back in — downloads and uploads of documents and data.
Overview
One plugin covers both directions of file transfer, and everything happens in the visitor's browser — no server involved:
- Export —
x-exportturns any element into a download button. The whole page, a chosen section, or an$xdata source can be saved as PDF, PNG, JPEG, WebP, CSV, or JSON. - Import —
x-importturns any element into an "open file" button. A JSON or CSV file from the visitor's device is read and handed back as usable data.
Together they close the loop: whatever a visitor exports, they (or anyone else) can import back. Save files, backups, and user-made content packs all work without a single line of server code.
Setup
Import/Export is included in manifest.js with all core plugins, or can be selectively loaded. The plugin name is export; import also works and loads the same file.
<script src="https://cdn.jsdelivr.net/npm/mnfst@latest/lib/manifest.min.js"></script>
Export
The x-export directive and $export magic save the page, a section of it, or a data source as a file. PDFs go through the browser's own print dialog — the familiar "Save as PDF" flow — so text stays selectable and copy-pasteable, long content flows across pages properly, and the page's own @media print styles apply. Exporting the whole page prints the whole page; exporting a target section temporarily narrows the print to just that section. Image formats (PNG, JPEG, WebP) use html2canvas-pro, fetched from the jsDelivr CDN only when someone first clicks.
Triggers
Buttons
Add x-export to anything clickable. With no options it saves the whole page as a PDF; a modifier picks another format.
<!-- PDF -->
<button x-export>PDF</button>
<!-- Image formats -->
<button x-export.png>PNG</button>
<button x-export.jpg>JPG</button>
<button x-export.webp>WEBP</button>
<!-- Data formats -->
<div x-data="{ rows: [
{ id: 1, name: 'Acme Co.', plan: 'Pro', mrr: 240 },
{ id: 2, name: 'Globex', plan: 'Starter', mrr: 49 }
] }">
<button x-export="{ format: 'csv', data: rows, filename: 'rows.csv' }">CSV</button>
<button x-export="{ format: 'json', data: rows, filename: 'rows.json' }">JSON</button>
</div>
The visual formats (PDF, PNG, JPEG, WebP) take a picture of the page or a chosen element. The data formats (CSV, JSON) write out a $x source or an inline value — covered in Data Sources below.
Anything that shouldn't appear in the saved file (headers, sidebars, navigation, the export button itself) can be marked with data-no-export. The filter applies to every visual export on the page.
The downloaded filename can be set without any other options: a data-filename attribute works on any element, and on a link the standard HTML download attribute works too.
<button x-export.png data-filename="report.png">Download report</button>
<a x-export.pdf href="#chart" download="chart.pdf">Save chart</a>
To control the sharpness or size of a saved image, use the options form. resolution multiplies the pixel density — it defaults to the visitor's screen density, so the saved image looks like what they see; set it to 2 or 3 for a consistently sharp result on any hardware. width and height set exact output dimensions.
<button x-export="{ format: 'png', resolution: 2 }">Retina PNG</button>
<button x-export="{ format: 'jpeg', width: 1200 }">1200 px wide JPG</button>
<button x-export="{ format: 'webp', width: 1200, height: 800 }">Sized WEBP</button>
For more control, pass an options object. target is a CSS selector pointing at the element to save; everything outside it is left out.
<div id="report">
<h2>Quarterly Report</h2>
<p>...</p>
</div>
<button x-export="{ format: 'pdf', target: '#report', filename: 'q3-report.pdf' }">Download PDF report</button>
Anchor Links
When x-export sits on a link whose href starts with #, that fragment becomes the target. Clicking downloads the matched element instead of scrolling to it.
<a x-export.png href="#chart">Save chart as PNG</a>
This pairs well with a normal in-page anchor as a "download this section" companion.
Cross-Page Links
When x-export sits on a link to another page, the link quietly gains ?export=<format>. The browser navigates as usual, and the destination page notices the signal in its URL and exports itself once it has loaded.
<!-- The link becomes /reports/q3?export=pdf -->
<a x-export.pdf href="/reports/q3">Download Q3 report</a>
The link says what the visitor wants; the destination page knows what it can hand over. Ordinary visitors never trigger downloads — only someone arriving through an export link, or a pasted URL carrying the parameter, will.
Data Sources
For tables and structured data, point at a $x source by name. The plugin writes the source out as CSV or JSON and downloads it.
<!-- Full source as CSV -->
<button x-export="{ format: 'csv', source: 'customers', filename: 'customers.csv' }">
Export customers
</button>
<!-- Filtered subset using existing query helpers -->
<button x-export="{ format: 'csv', data: $x.customers.$search(term, 'name') }">
Export search results
</button>
<!-- Full JSON tree -->
<button x-export="{ format: 'json', source: 'settings' }">
Export settings
</button>
CSV quoting is handled automatically to the standard rules (RFC 4180): values containing commas, quotes, or line breaks come out intact. The header row collects every column that appears in any row, so rows don't all need the same fields.
Export Magic
$export does everything x-export does, from inside an expression — useful when the download should happen on your own conditions: after validation, behind a sign-in check, or partway through a multi-step flow.
<!-- Validate, then export -->
<form @submit.prevent="$refs.form.checkValidity() && await $export({ format: 'csv', data: rows })">
...
</form>
<!-- Require sign-in before export -->
<button @click="$auth.isAuthenticated && $export({ format: 'pdf', target: '#dashboard' })">
Download (signed-in only)
</button>
All of $export's options are identical to those of x-export.
Export Options
| Property | Type | Default | Description |
|---|---|---|---|
format |
String | 'pdf' |
One of pdf, png, jpeg, webp, csv, json |
target |
String / Element | <body> |
CSS selector or element to save. Visual formats only. |
source |
String | — | Name of a $x data source to export. csv / json only. |
data |
Array / Object | — | Inline data to export instead of a $x source. csv / json only. |
filename |
String | export-<timestamp>.<ext> |
Suggested download name. Falls back to a download attribute on links, then a data-filename attribute, then a timestamped default. |
resolution |
Number | screen density | Sharpness multiplier for saved images. The default matches the visitor's screen — 1 on standard monitors, 2 on retina — so the file looks like what they see. Set it explicitly (e.g. 2 or 3) for the same sharpness on any hardware. |
width |
Number | natural width | Output width in pixels for saved images. Overrides the element's natural width. |
height |
Number | natural height | Output height in pixels for saved images. Overrides the element's natural height. |
quality |
Number (0–1) | 0.95 |
JPEG / WebP compression quality |
backgroundColor |
String | page background | Solid background for saved images (PNG, JPEG, WebP). Defaults to the page's own background. Pass 'transparent' for no fill (useful for icon / logo exports). PDFs use the page's CSS directly and ignore this option. |
pageSize |
String | 'a4' |
PDF page size — a4, a3, letter, legal, etc. Visitors can still change it in the browser's print dialog. |
trigger |
String | 'click' |
'click' (default) or 'url'. With 'url' the export runs on page load if the URL carries the export parameter. |
urlParam |
String | 'export' |
Name of the URL parameter to watch when trigger: 'url' |
delay |
Number | 0 |
Milliseconds to wait after a url trigger before saving. Useful when charts or animations need a moment to settle. |
Batch and CI Exports
The mnfst-export command-line tool runs the same exports with nobody clicking — the right tool for build pipelines, scheduled jobs, and bulk runs. It supports the same six formats, plus an rss format for blog feeds.
# Snapshot a single route as PDF
npx mnfst-export --pdf --path /reports/q3 --target "#report"
# Whole project at once (reads manifest.export.routes from manifest.json)
npx mnfst-export
# Data source as CSV
npx mnfst-export --csv --path /admin/customers --source customers
Routes can be listed in manifest.json so the same npx mnfst-export runs anywhere.
manifest.json {
"export": {
"output": "exports",
"routes": [
{ "path": "/reports/q3", "format": "pdf", "target": "#report" },
{ "path": "/customers", "format": "csv", "source": "customers" },
{ "path": "/blog", "format": "rss", "source": "posts", "map": { "link": "slug" } }
]
}
}
| Option | Type | Default | Description |
|---|---|---|---|
output |
string |
"exports" |
Output folder relative to the project root |
routes |
object[] |
[] |
Per-route export entries. Each takes the same fields as the directive's options (path, format, target, source, filename, pageSize, etc.) |
rss |
object |
inherited | Channel defaults: { title, link, description }. Falls back to manifest.name, manifest.live_url, and manifest.description. |
The tool starts a temporary local server, opens each page in an invisible browser, waits for the page to finish rendering (the manifest:render-ready signal), then saves whatever that page exposes. PDFs and images use the browser engine's own capture, which is more reliable there than the in-page libraries.
Run npx mnfst-export --help for the full list of flags. Puppeteer (the invisible browser) is required — install it once in the project.
npm i -D puppeteer
Import
The x-import directive and $import magic bring files back in. The visitor picks a JSON or CSV file, the plugin reads it right there in the browser (the file never leaves their device), and the content arrives as an event, a promise, or straight into an $x data source. Restoring an exported backup, loading a saved document, or accepting user-made content packs is one attribute.
With no options the picker accepts JSON or CSV and tells them apart by the file's extension; a modifier pins one format.
<button x-import>Open file</button>
<button x-import.json>Open JSON</button>
<button x-import.csv>Open CSV</button>
The result arrives as a manifest:import event on the trigger element, so it can be handled right where the button is. The event's detail carries data (the parsed content), format, source (when one was targeted), and file (name, size, type). A file that can't be read fires manifest:import-error instead, with format and error.
Here's the whole loop in one place. Download the table as a CSV, then import the file back — a second table appears showing exactly what the file contains, parsed fresh from disk. Change a name or a number in a spreadsheet app first if you'd like proof it isn't a trick.
<div x-data="{ team: [
{ name: 'June', role: 'Design', hours: 12 },
{ name: 'Marco', role: 'Engineering', hours: 9 },
{ name: 'Priya', role: 'Research', hours: 14 }
], imported: null, filename: '' }">
<table>
<thead><tr><th>Name</th><th>Role</th><th>Hours</th></tr></thead>
<tbody>
<template x-for="(m, i) in team" :key="i">
<tr>
<td x-text="m.name"></td>
<td x-text="m.role"></td>
<td x-text="m.hours"></td>
</tr>
</template>
</tbody>
</table>
<button x-export="{ format: 'csv', data: team, filename: 'team.csv' }">Download CSV</button>
<button x-import.csv
@manifest:import="imported = $event.detail.data; filename = $event.detail.file.name">
Import a CSV
</button>
<template x-if="imported">
<div>
<small x-text="'From ' + filename + ':'"></small>
<table>
<thead><tr><th>Name</th><th>Role</th><th>Hours</th></tr></thead>
<tbody>
<template x-for="(m, i) in imported" :key="i">
<tr>
<td x-text="m.name"></td>
<td x-text="m.role"></td>
<td x-text="m.hours"></td>
</tr>
</template>
</tbody>
</table>
</div>
</template>
</div>
Replacing a Data Source
Pass a source to pour the file's content into a $x data source, replacing what's there. Anything on the page bound to that source updates on its own. A bare string value is shorthand for the same thing.
<button x-import="{ source: 'products' }">Load products</button>
<button x-import="'products'">Load products</button>
Together with export, backup and restore is a few lines — no server, no account:
<button x-export="{ format: 'json', source: 'saves', filename: 'backup.json' }">Back up</button>
<button x-import="{ format: 'json', source: 'saves' }">Restore</button>
Import Magic
$import(opts) opens the picker from inside an expression and hands back the parsed data — or null when the visitor closes the dialog without choosing. Same options as the directive.
<button @click="save = await $import({ format: 'json' })">Load save</button>
CSV Parsing
A CSV file comes back as a list of objects, one per row, named by the header row. Quotes, and commas or line breaks inside values, follow the standard CSV rules, and the separator — comma, semicolon, or tab — is detected automatically from the first line. Values arrive typed: numbers become numbers, true/false/null become real values, cells that look like JSON ({…} or […]) are parsed, and everything else stays text. A file produced by the CSV export imports back exactly.
Import Options
| Property | Type | Default | Description |
|---|---|---|---|
format |
String | inferred | 'json' or 'csv'. Left out, it's inferred from the file extension, defaulting to JSON. |
source |
String | — | $x data source to replace with the file's content. |
accept |
String | by format | Override which file types the picker dialog offers. |
Article does not exist
There is no documentation at this path.