Export + Print

Enterprise feature pack: download to Excel, PDF, CSV, TSV, HTML, or open a printable view in a new window. (requires @svgrid/enterprise)

A live, editable Svelte 5 data grid example from the SvGrid gallery (Data Export & Import). See the SvGrid documentation for the full API.

About this example

The export feature pack of the Svelte 5 data grid: download the visible grid to Excel, PDF, CSV, TSV or HTML, or open a printable view in a new window. The grid is plain @svgrid/grid; installEnterprise(api) adds api.exportData(...) and api.print(...) onto the same SvGridApi you already hold, so the export buttons are a few lines each and respect the current sort, filters and row selection.

Demonstrates the @svgrid/enterprise feature pack: download the visible grid to Excel, PDF, CSV, TSV, or HTML, and open a printable view in a new window.

The grid itself is plain @svgrid/grid. Pro is installed via installEnterprise(api) which adds api.exportData(...) and api.print(...) onto the same SvGridApi object you already have.

Imports, features and API used

Imports: @svgrid/grid, @svgrid/enterprise, ../shared/seed

Table features registered: rowSortingFeature, columnFilteringFeature, rowSelectionFeature

Columns: company (Company), product (Product), sellDate (Sell date), quantity (Quantity), orderId (Order ID), country (Country), price (Price), company (Company), product (Product), sellDate (Sell date), quantity (Quantity), orderId (Order ID)

SvGridApi methods called: api.exportData(), api.print()

Frequently asked questions

How do I add export to a grid?

In onApiReady call installEnterprise(api) from @svgrid/enterprise and keep the returned api. Then api.exportData({ format: 'xlsx' }) downloads the file; 'pdf', 'csv', 'tsv' and 'html' are the other formats.

Is CSV export free?

Yes. api.exportCsv and exportTsv are in the MIT @svgrid/grid package and export values as shown on screen. Excel, PDF and the styled formats are the enterprise part.

What does print do?

api.print() opens a new window with the grid rendered as a plain table with a title and formatted values and calls the browser's print dialog, which is also the route to Save as PDF without a library.

Related documentation

Related articles

Source code (21-export-and-print.svelte)

<script lang="ts">
  /**
   * 21. Export + Print (Pro)
   * ------------------------
   * Demonstrates the @svgrid/enterprise feature pack: download the visible grid to
   * Excel, PDF, CSV, TSV, or HTML, and open a printable view in a new window.
   *
   * The grid itself is plain @svgrid/grid. Pro is installed via
   * installEnterprise(api) which adds api.exportData(...) and api.print(...) onto
   * the same SvGridApi object you already have.
   */
  import {
    SvGrid,
    tableFeatures,
    rowSortingFeature,
    columnFilteringFeature,
    rowSelectionFeature,
    type GridColumns,
    type SvGridApi,
  } from '@svgrid/grid'
  import {
    installEnterprise,
    setLicenseKey,
    clearLicenseKey,
    dismissUnlicensedNudge,
    type EnterpriseGridApi,
  } from '@svgrid/enterprise'
  import { makeOrders, type Order } from '../shared/seed'

  // Development license. In production, customers set their own SVENTERPRISE-...
  // key once at app startup (e.g. in main.ts). Toggle below to see the
  // unlicensed soft-gate (watermark in the grid + console.log nudge).
  let licensed = $state(true)
  $effect(() => {
    if (licensed) {
      setLicenseKey('SVENTERPRISE-DEV-LOCAL')
      dismissUnlicensedNudge()
    } else {
      clearLicenseKey()
    }
  })

  const features = tableFeatures({
    rowSortingFeature,
    columnFilteringFeature,
    rowSelectionFeature,
  })

  let rows = $state<Order[]>(makeOrders(120))
  let api = $state<EnterpriseGridApi<typeof features, Order> | null>(null)
  let lastAction = $state<string>('')
  let busy = $state<string | null>(null)
  let errorMsg = $state<string | null>(null)

  const columns: GridColumns<Order> = [
    { field: 'company',  header: 'Company',  width: 140 },
    { field: 'product',  header: 'Product',  width: 170 },
    { field: 'sellDate', header: 'Sell date', width: 110,
      format: { type: 'date', pattern: 'y-m-d' } },
    { field: 'quantity', header: 'Quantity', width: 90,
      format: { type: 'number', options: { maximumFractionDigits: 0 } } },
    { field: 'orderId',  header: 'Order ID', width: 130 },
    { field: 'country',  header: 'Country',  width: 90 },
    { field: 'price',    header: 'Price',    width: 110,
      format: { type: 'currency', currency: 'USD' } },
  ]

  // Listed explicitly (rather than .map'd from `columns`) so the export
  // module's ExportColumn type (field: string, required) is satisfied
  // without casts - ColumnDef.field is keyof TData | undefined.
  const exportColumns = [
    { field: 'company',  header: 'Company' },
    { field: 'product',  header: 'Product' },
    { field: 'sellDate', header: 'Sell date' },
    { field: 'quantity', header: 'Quantity' },
    { field: 'orderId',  header: 'Order ID' },
    { field: 'country',  header: 'Country' },
    { field: 'price',    header: 'Price' },
  ]

  function onReady(next: SvGridApi<typeof features, Order>) {
    api = installEnterprise(next)
  }

  async function run(label: string, fn: () => Promise<unknown>) {
    if (!api) return
    busy = label
    errorMsg = null
    try {
      await fn()
      lastAction = label
    } catch (err) {
      errorMsg = err instanceof Error ? err.message : String(err)
    } finally {
      busy = null
    }
  }

  const exportFormats: Array<{ label: string; format: 'xlsx' | 'pdf' | 'csv' | 'tsv' | 'html' }> = [
    { label: 'Excel (xlsx)', format: 'xlsx' },
    { label: 'PDF',          format: 'pdf' },
    { label: 'CSV',          format: 'csv' },
    { label: 'TSV',          format: 'tsv' },
    { label: 'HTML',         format: 'html' },
  ]
</script>

<section class="flex flex-col flex-1 min-h-0 gap-3">
  <div class="text-sm shrink-0 xp-note">
    {rows.length} rows. Apply sort or filter - exports always reflect the
    <em>currently displayed</em> rows. Print opens a new window with the
    same view, ready for the browser print dialog.
  </div>

  <div class="flex flex-wrap items-center gap-2 shrink-0">
    {#each exportFormats as f (f.format)}
      <button
        type="button"
        class="rounded border px-3 py-1.5 text-sm font-medium disabled:opacity-50 xp-btn"
        disabled={busy !== null || api === null}
        onclick={() =>
          run(`Exported ${f.label}`, () =>
            api!.exportData({
              format: f.format,
              filename: `orders.${f.format}`,
              columns: exportColumns,
              pageOrientation: 'landscape',
            }),
          )}
      >
        Export {f.label}
      </button>
    {/each}
    <button
      type="button"
      class="rounded border px-3 py-1.5 text-sm font-medium disabled:opacity-50 xp-btn xp-btn-primary"
      disabled={busy !== null || api === null}
      onclick={() =>
        run('Opened print view', () =>
          api!.print({
            title: 'Orders',
            columns: exportColumns,
            orientation: 'landscape',
          }),
        )}
    >
      Print…
    </button>
    {#if busy}
      <span class="text-xs xp-note">{busy}…</span>
    {:else if lastAction}
      <span class="text-xs text-green-600 dark:text-green-400">{lastAction}</span>
    {/if}
    {#if errorMsg}
      <span class="text-xs text-red-600 dark:text-red-400">{errorMsg}</span>
    {/if}

    <label class="ml-auto flex items-center gap-2 text-xs xp-note">
      <input type="checkbox" bind:checked={licensed} class="h-4 w-4" />
      Licensed (uncheck to see the unlicensed watermark + console nudge)
    </label>
  </div>

  <div class="flex-1 min-h-0">
    <SvGrid responsive={true}
      columnResize
      data={rows}
      columns={columns}
      features={features}
      filterMode="menu"
      selectionMode="both"
      showRowSelection={true}
      showRowNumbers={true}
      showGroupingControls={false}
      enableCellSelection={true}
      enableInlineEditing={false}
      rowHeight={36}
      containerHeight="100%"
      fitColumns={true}
      onApiReady={onReady}
    />
  </div>

  <footer class="text-xs shrink-0 xp-note">
    Pro feature - gated by <code>setLicenseKey()</code>. Without a valid key
    (prefix <code>SVENTERPRISE-</code>), the feature still runs but the grid shows
    a watermark linking to jqwidgets.com. Revoked or malformed keys throw.
  </footer>
</section>

<style>
  .xp-note { color: var(--sg-muted, #475569); }
  .xp-btn {
    border-color: var(--sg-border, #cbd5e1);
    background: var(--sg-bg, #ffffff);
    color: var(--sg-fg, #0f172a);
  }
  .xp-btn:hover:not(:disabled) { background: var(--sg-row-hover-bg, #f8fafc); }
  .xp-btn-primary {
    border-color: var(--sg-accent, #4f46e5);
    background: var(--sg-accent, #4f46e5);
    color: var(--sg-on-accent, #fff);
  }
  .xp-btn-primary:hover:not(:disabled) {
    background: var(--sg-accent, #4f46e5);
    filter: brightness(1.08);
  }
</style>

View this example on GitHub

More Data Export & Import examples

  • Excel / CSV import - File picker + column mapping + per-row validation preview before commit. Reads xlsx / csv / tsv / json with format auto-detect.
  • Export - Theme-matched - One xlsx, light or dark - styles read from the same --sg-* tokens the grid renders with.
  • Export - Header + Footer + Logo - Branded xlsx: PNG logo + title + subtitle in the page header, generated date + page numbers in the footer.
  • Export - Cell images - Product grid with thumbnail column. On xlsx export each thumbnail is embedded as a real picture cell.
  • Export - Multiple sheets - One xlsx with 5 tabs - All orders + per-region splits - independent of the current grid filter.