
Export to SPSS (.sav)
The SPSS extension converts a Jspreadsheet or raw JSON dataset into an SPSS system file (.sav), which can be opened directly in SPSS, PSPP, or any tool that reads the PSPP System File Format. Column types, formatting, value labels, variable labels, display width, alignment and measure levels are automatically converted according to the spreadsheet configuration.
Supports client-side and Node.js platforms
This extension writes to a pure binaryUint8Array, so it can be used to download files in a browser or persist files to disk from a backend script.
Documentation
Available settings
| Property | Description |
|---|---|
| filename: string | The filename used by the download assistant. Default: data.sav |
| fileLabel: string | Optional file label (maximum 64 characters) embedded in the .sav header. If not specified, it falls back to worksheetName. |
| headers: string[] | Column headers when passing a raw array of data instead of a worksheet instance. |
| columns: object[] | Column definitions (type, format, source, width, etc.) used when passing a raw array of data. |
| comments: object | array | Cell comments to retain in the .sav document record. Can be an array of { cell, comment } or a mapping of { "A1": "text" }. |
Type mapping
This extension maps each column type to the corresponding SPSS variable definition:
| Jspreadsheet column type | SPSS variable | Measure | Description |
|---|---|---|---|
number, numeric, autonumber |
Numeric (F) |
Scale | Width / decimal places inferred from the column format / mask |
percent |
Numeric (PCT) |
Scale | Decimal places inferred from the format |
calendar |
Numeric (DATE11 / DATETIME22) |
Scale | Converted to seconds since 1582-10-14. A time component triggers DATETIME. |
rating, progressbar |
Numeric | Ordinal | |
checkbox, radio |
Numeric (0 / 1) | Nominal | Value labels 0 = No, 1 = Yes automatically written |
dropdown, autocomplete with { id, name } source (numeric IDs) |
Numeric | Nominal | Source written as SPSS value labels |
dropdown, autocomplete (string source) |
String (A) |
Nominal | Written in SPSS value-label form |
text, html, email, url, tags, color, image, notes |
String (A) |
Nominal | Width automatically increases according to the longest cell; long strings use continuation records |
Methods
After registration, two helper methods are added to each worksheet, and the extension itself also exposes equivalent static methods:
| Method | Description |
|---|---|
worksheet.downloadSav(options?) |
Generates a .sav file and triggers the browser to download it. |
worksheet.generateSav(options?) |
Generates a .sav binary file and returns it as a Uint8Array. |
spss.download(worksheet, options?) |
Static version of downloadSav. |
spss.generate(worksheet, options?) |
Static version of generateSav. Also accepts a raw data[][] array. |
Installation
Choose one of the following options:
Using NPM
$ npm install @jspreadsheet/spss
Using a CDN
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/spss/dist/index.js"></script>
SPSS limitations
When exporting to .sav, be aware of the fixed constraints imposed by the SPSS System File Format. The extension handles these transparently, but some values may be coerced:
- Allowed characters: Variable names only accept
A-Z,a-z,0-9,@,#,$,_and.. Invalid characters are replaced with_. - String width: SPSS stores strings as 8-byte segments. Strings longer than 255 characters are written using continuation records.
- Value labels: Each label is limited to 120 bytes. Numeric value labels are only emitted when the data source uses
{ id, name }pairs with numeric IDs. - Dates: Calendar columns are stored as SPSS numeric date values (seconds since October 14, 1582). When the column
formatcontains a time component (H/h), theDATETIME22format is used; otherwise,DATE11. - Missing values: Empty cells are converted to the SPSS system-missing value (SYSMIS).
- Encoding: The file is written in UTF-8 (character code 65001).
Code examples
Creating a .sav file using JavaScript
How to export a spreadsheet to an SPSS .sav file.
<html>
<script src="https://jspreadsheet.com/v12/jspreadsheet.js"></script>
<script src="https://jsuites.net/v6/jsuites.js"></script>
<link rel="stylesheet" href="https://jspreadsheet.com/v12/jspreadsheet.css" type="text/css" />
<link rel="stylesheet" href="https://jsuites.net/v6/jsuites.css" type="text/css" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons" />
<script src="https://cdn.jsdelivr.net/npm/@jspreadsheet/spss/dist/index.js"></script>
<div id='spreadsheet'></div>
<p><input type="button" value="Download .sav" id="btn1" /></p>
<script>
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// This license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate an extended demo license.
jspreadsheet.setLicense('NDI4OWE2ZDNjMDBjYjBjNGYwZWYyYTc5OWNiMDY0MmU1ZTgxZWNjNzZlNjY3YjIyMTE2MmQwN2NlN2NmMTJlMmUwNDlhMTU1ZTczOTA3OTk0ZGM1NzBkZDI4NmEyZDRiZGNhZjExNDI1MTkwOTNkNWM1MmIyYzM3ODhiMjMwZjMsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjM01EQXhPVGszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
// Spreadsheet plugin
jspreadsheet.setExtensions({ spss });
// Create a spreadsheet
let worksheets = jspreadsheet(document.getElementById('spreadsheet'), {
worksheets: [{
worksheetName: 'Survey',
columns: [
{ type: 'text', title: 'Name', width: 160 },
{ type: 'number', title: 'Age', format: '0' },
{ type: 'dropdown', title: 'Gender', source: [
{ id: 1, name: 'Female' },
{ id: 2, name: 'Male' },
{ id: 3, name: 'Other' },
]},
{ type: 'calendar', title: 'Date of Birth', format: 'DD/MM/YYYY' },
{ type: 'checkbox', title: 'Subscribed' },
],
data: [
['Alice', 34, 1, '1990-04-12', true],
['Bob', 27, 2, '1997-11-02', false],
['Carol', 41, 1, '1983-06-20', true],
],
}],
});
document.getElementById("btn1").onclick = function() {
worksheets[0].downloadSav({ filename: 'survey.sav' });
}
</script>
</html>
import React, { useRef } from "react";
import { Spreadsheet, Worksheet } from "@jspreadsheet/react";
import jspreadsheet from "jspreadsheet";
import spss from "@jspreadsheet/spss";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// This license is valid for one day, after which the spreadsheet will become read-only.
// For a longer trial period, you can create a free account and generate a demo license with a longer validity period.
jspreadsheet.setLicense('NDI4OWE2ZDNjMDBjYjBjNGYwZWYyYTc5OWNiMDY0MmU1ZTgxZWNjNzZlNjY3YjIyMTE2MmQwN2NlN2NmMTJlMmUwNDlhMTU1ZTczOTA3OTk0ZGM1NzBkZDI4NmEyZDRiZGNhZjExNDI1MTkwOTNkNWM1MmIyYzM3ODhiMjMwZjMsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjM01EQXhPVGszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
// JSS data grid plugin
jspreadsheet.setExtensions({ spss });
export default function App() {
// Spreadsheet worksheet array
const spreadsheet = useRef();
const download = function() {
spreadsheet.current[0].downloadSav({ filename: 'survey.sav' });
}
// Rendering component
return (
<>
<Spreadsheet ref={spreadsheet}>
<Worksheet minDimensions={[6, 6]} />
</Spreadsheet>
<input type="button" value="Download .sav" onClick={() => download()} />
</>
);
}
<template>
<Spreadsheet ref="spreadsheet" :license="license" :extensions="extensions">
<Worksheet :minDimensions="[10,10]" />
</Spreadsheet>
<input type="button" value="Download .sav" @click="download" />
</template>
<script>
import { Spreadsheet, Worksheet, jspreadsheet } from "@jspreadsheet/vue";
import spss from "@jspreadsheet/spss";
import "jsuites/dist/jsuites.css";
import "jspreadsheet/dist/jspreadsheet.css";
// Define the data grid license
const license = 'NDI4OWE2ZDNjMDBjYjBjNGYwZWYyYTc5OWNiMDY0MmU1ZTgxZWNjNzZlNjY3YjIyMTE2MmQwN2NlN2NmMTJlMmUwNDlhMTU1ZTczOTA3OTk0ZGM1NzBkZDI4NmEyZDRiZGNhZjExNDI1MTkwOTNkNWM1MmIyYzM3ODhiMjMwZjMsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjM01EQXhPVGszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9';
// Define the data grid extensions
const extensions = { spss };
export default {
components: {
Spreadsheet,
Worksheet,
},
methods: {
download() {
// Worksheet instance
this.$refs.spreadsheet.current[0].downloadSav({ filename: 'survey.sav' });
}
},
data() {
return {
license,
extensions,
};
}
}
</script>
import { Component, ViewChild, ElementRef } from "@angular/core";
import jspreadsheet from "jspreadsheet";
import spss from "@jspreadsheet/spss";
// You can use the following license for quick testing on localhost, StackBlitz, or CodeSandbox.
// This license is valid for one day, after which the spreadsheet will become read-only.
jspreadsheet.setLicense('NDI4OWE2ZDNjMDBjYjBjNGYwZWYyYTc5OWNiMDY0MmU1ZTgxZWNjNzZlNjY3YjIyMTE2MmQwN2NlN2NmMTJlMmUwNDlhMTU1ZTczOTA3OTk0ZGM1NzBkZDI4NmEyZDRiZGNhZjExNDI1MTkwOTNkNWM1MmIyYzM3ODhiMjMwZjMsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjM01EQXhPVGszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9');
// Extend
jspreadsheet.setExtensions({ spss });
@Component({
standalone: true,
selector: "app-root",
template: `<div #spreadsheet></div>
<input type="button" value="Download .sav" (click)="this.export()" />`
})
export class AppComponent {
@ViewChild("spreadsheet") spreadsheet: ElementRef;
// Worksheets
worksheets: jspreadsheet.worksheetInstance[];
// Create a new data grid
ngAfterViewInit() {
// Create a spreadsheet
this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {
worksheets: [
{ minDimensions: [6, 6] }
]
});
}
export() {
// Worksheet instance
this.worksheets[0].downloadSav({ filename: 'survey.sav' });
}
}
Backend .sav export
This extension also works in Node.js — generate returns a raw Uint8Array that you can persist directly to disk without rendering the DOM.
const jspreadsheet = require('jspreadsheet');
const spss = require('@jspreadsheet/spss');
const { writeFile } = require('node:fs/promises');
jspreadsheet.setLicense({
clientId: '356a192b7913b04c54574d18c28d46e6395428ab',
licenseKey: 'NDI4OWE2ZDNjMDBjYjBjNGYwZWYyYTc5OWNiMDY0MmU1ZTgxZWNjNzZlNjY3YjIyMTE2MmQwN2NlN2NmMTJlMmUwNDlhMTU1ZTczOTA3OTk0ZGM1NzBkZDI4NmEyZDRiZGNhZjExNDI1MTkwOTNkNWM1MmIyYzM3ODhiMjMwZjMsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpjM01EQXhPVGszTENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0p6ZEdGamEySnNhWFI2TG1sdklpd2lkMlZpWTI5dWRHRnBibVZ5TG1sdklpd2liRzlqWVd4b2IzTjBJbDBzSW5Cc1lXNGlPaUl6TkNJc0luTmpiM0JsSWpwYkluWTNJaXdpZGpnaUxDSjJPU0lzSW5ZeE1DSXNJbll4TVNJc0luWXhNaUlzSW1Ob1lYSjBjeUlzSW1admNtMXpJaXdpWm05eWJYVnNZU0lzSW5CaGNuTmxjaUlzSW5KbGJtUmxjaUlzSW1OdmJXMWxiblJ6SWl3aWFXMXdiM0owWlhJaUxDSmlZWElpTENKMllXeHBaR0YwYVc5dWN5SXNJbk5sWVhKamFDSXNJbkJ5YVc1MElpd2ljMmhsWlhSeklpd2lZMnhwWlc1MElpd2ljMlZ5ZG1WeUlpd2ljMmhoY0dWeklpd2labTl5YldGMElpd2ljR2wyYjNRaVhTd2laR1Z0YnlJNmRISjFaWDA9'
});
jspreadsheet.setExtensions({ spss });
let spreadsheet = jspreadsheet(null, {
worksheets: [{
worksheetName: 'Survey',
columns: [
{ type: 'text', title: 'Name' },
{ type: 'number', title: 'Age', format: '0' },
{ type: 'calendar', title: 'Date of Birth', format: 'DD/MM/YYYY' },
],
data: [
['Alice', 34, '1990-04-12'],
['Bob', 27, '1997-11-02'],
],
}],
});
const binary = spreadsheet[0].generateSav({ fileLabel: 'Survey' });
await writeFile('survey.sav', binary);
The spss.generate function also accepts raw arrays of data and columns, so you can generate SPSS files without instantiating a spreadsheet.
const spss = require('@jspreadsheet/spss');
const { writeFile } = require('node:fs/promises');
const binary = spss.generate([
['Alice', 34, 1],
['Bob', 27, 2],
['Carol', 41, 1],
], {
headers: ['Name', 'Age', 'Gender'],
columns: [
{ type: 'text', name: 'Name' },
{ type: 'number', name: 'Age', format: '0' },
{ type: 'dropdown', name: 'Gender', source: [
{ id: 1, name: 'Female' },
{ id: 2, name: 'Male' },
]},
],
fileLabel: 'Survey',
});
await writeFile('survey.sav', binary);