Export - Header + Footer + Logo

Branded xlsx: PNG logo + title + subtitle in the page header, generated date + page numbers in the footer. (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

A branded Excel export from the Svelte 5 data grid: a single-sheet xlsx that opens with a page header holding a PNG logo, the company name and a report subtitle, and a three-segment footer with the generated date on the left, the brand URL in the centre and Excel page-number fields on the right. The lines are passed as ExportHeaderFooterLine entries, and image lines are embedded as real pictures in the workbook.

Generates a single-sheet xlsx that opens with a branded page header (PNG logo + company name + report subtitle) and a three-segment footer (left: generated date, center: brand URL, right: Excel page-number macros). All lines are forwarded via ExportHeaderFooterLine[] and the wrapper translates them into Smart's headerContent rows; image lines route through addImageToCell to embed a real picture.

Imports, features and API used

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

Table features registered: rowSortingFeature, columnFilteringFeature

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

SvGridApi methods called: api.exportData()

Frequently asked questions

How do I add a logo to the export?

Pass an image line in the header array of exportData with the PNG as a data URL or fetched bytes. The exporter embeds it in the workbook's media and anchors it in the header area.

How do page numbers work?

The footer's right segment carries Excel's page-number and page-count fields, which Excel resolves when printing, so a long sheet numbers itself.

Can the header be text only?

Yes. Any mix of text and image lines is accepted; omit the image line for a plain title and subtitle.

Related articles

Source code (57-export-header-footer-logo.svelte)

<script lang="ts">
  /**
   * 57. Export - branded xlsx (header + footer + logo) (Pro)
   * -------------------------------------------------------
   * Generates a single-sheet xlsx that opens with a branded page header
   * (PNG logo + company name + report subtitle) and a three-segment footer
   * (left: generated date, center: brand URL, right: Excel page-number
   * macros). All lines are forwarded via `ExportHeaderFooterLine[]` and the
   * wrapper translates them into Smart's `headerContent` rows; image lines
   * route through `addImageToCell` to embed a real picture.
   */
  import { onMount } from 'svelte'
  import {
    SvGrid,
    tableFeatures,
    rowSortingFeature,
    columnFilteringFeature,
    type GridColumns,
  } from '@svgrid/grid'
  import {
    installEnterprise,
    setLicenseKey,
    type EnterpriseGridApi,
    type ExportHeaderFooterLine,
  } from '@svgrid/enterprise'
  import { makeOrders, type Order } from '../shared/seed'

  setLicenseKey('SVENTERPRISE-DEV-DEMO')

  const features = tableFeatures({ rowSortingFeature, columnFilteringFeature })
  const rows = makeOrders(120)
  let api = $state<EnterpriseGridApi<typeof features, Order> | null>(null)
  let busy = $state(false)
  let logoPng = $state<string | null>(null)

  // Rasterise the SVG mark to PNG once. Excel doesn't render embedded SVG
  // reliably, so we paint it to a canvas and read back a PNG data URL.
  onMount(async () => {
    const svg = `
      <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
        <rect width='64' height='64' rx='12' fill='#2563eb'/>
        <text x='32' y='42' text-anchor='middle' fill='#fff'
              font-family='Inter, sans-serif' font-size='28' font-weight='800'>SG</text>
      </svg>`
    const svgUrl = `data:image/svg+xml;base64,${btoa(svg.trim())}`
    const img = new Image()
    img.src = svgUrl
    await new Promise<void>((resolve) => { img.onload = () => resolve() })
    const cv = document.createElement('canvas')
    cv.width = 64; cv.height = 64
    cv.getContext('2d')!.drawImage(img, 0, 0, 64, 64)
    logoPng = cv.toDataURL('image/png')
  })

  async function doExport() {
    if (!api || !logoPng) return
    busy = true
    try {
      const header: ExportHeaderFooterLine[] = [
        { image: logoPng, width: 64, height: 64 },
        { text: 'SvGrid Industries Inc.',
          style: { fontWeight: 'bold', fontSize: 18, color: '#0f172a' } },
        { text: `Q${Math.ceil((new Date().getMonth() + 1) / 3)} ${new Date().getFullYear()} Orders - Internal`,
          style: { color: '#64748b', fontSize: 12 } },
      ]
      const footer: ExportHeaderFooterLine[] = [
        {
          left:   `Generated ${new Date().toISOString().slice(0, 10)}`,
          center: 'svgrid.com',
          right:  'Page &P of &N',
        },
      ]
      await api.exportData({
        format: 'xlsx',
        filename: 'orders-branded',
        header,
        footer,
      })
    } catch (err) {
      console.error('[export]', err)
    } finally {
      busy = false
    }
  }

  const columns: GridColumns<Order> = [
    { field: 'orderId', header: 'Order ID', editorType: 'text', width: 140 },
    { field: 'company', header: 'Company',  editorType: 'text', width: 180 },
    { field: 'product', header: 'Product',  editorType: 'text', width: 180 },
    {
      field: 'sellDate', header: 'Sell date', editorType: 'date', width: 120,
      format: { type: 'date', pattern: 'y-m-d' },
    },
    { field: 'quantity', header: 'Qty', editorType: 'number', width: 80 },
    {
      field: 'price', header: 'Price', editorType: 'number', width: 110,
      format: { type: 'currency', currency: 'USD' },
    },
    { field: 'country', header: 'Country', editorType: 'text', width: 110 },
  ]
</script>

<section class="flex flex-col flex-1 min-h-0 gap-3">
  <div class="flex flex-wrap items-center gap-3 text-sm shrink-0">
    <button
      type="button"
      onclick={doExport}
      disabled={busy || !logoPng}
      class="xp-btn inline-flex items-center gap-2 rounded-md px-3 py-1.5 font-medium disabled:opacity-50"
    >
      {#if logoPng}<img src={logoPng} alt="" class="h-5 w-5 rounded" />{/if}
      {busy ? 'Exporting…' : 'Download branded XLSX'}
    </button>
    <div class="xp-note">
      The logo above is embedded in the xlsx page header; the footer carries the date + Excel page-number macros.
    </div>
  </div>

  <div class="flex-1 min-h-0">
    <SvGrid responsive={true}
      columnResize
      data={rows}
      columns={columns}
      features={features}
      filterMode="menu"
      selectionMode="cell"
      showRowNumbers={true}
      showPagination={true}
      pageSize={25}
      enableInlineEditing={false}
      enableCellSelection={true}
      rowHeight={36}
      containerHeight="100%"
      fitColumns={true}
      onApiReady={(next) => (api = installEnterprise(next))}
    />
  </div>
</section>

<style>
  .xp-btn {
    border: 1px solid var(--sg-accent, #4f46e5);
    background: var(--sg-accent, #4f46e5);
    color: var(--sg-on-accent, #fff);
  }
  .xp-btn:hover:not(:disabled) { filter: brightness(1.08); }
  .xp-note { color: var(--sg-muted, #64748b); }
</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 + Print - Enterprise feature pack: download to Excel, PDF, CSV, TSV, HTML, or open a printable view in a new window.
  • Export - Theme-matched - One xlsx, light or dark - styles read from the same --sg-* tokens the grid renders with.
  • 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.