Command Palette
Search for a command to run...

Your first deck

Build a working Anki deck from an empty folder and import it into Anki. Covers project setup, installation under four package managers, and one complete script.

tutorial · beginner · 15m
applies to ankipack >= 0.3, anki 26.08
by Oliver Seifert & Claude ·

Your first deck

By the end of this you will have a folder containing one script, and an .apkg file that opens in Anki with three cards in it.

You need Anki to see the result, and either Bun or Node 22.18 or later.

Make it your deck
  • Deck name: {{ deck }}
  • Description: {{ about }}
  • Card 1 front: {{ front1 }}
  • Card 1 back: {{ back1 }}
  • Card 2 front: {{ front2 }}
  • Card 2 back: {{ back2 }}
  • Card 3 front: {{ front3 }}
  • Card 3 back: {{ back3 }}

Change any of those and every code block below updates as you read, so the deck you build is yours rather than mine. Leave them alone and you get a small Spanish deck.

1. Create an empty project

Start somewhere new rather than in an existing project:

mkdir my-deckcd my-deck

Create a package.json in it, starting with this:

{  "name": "my-deck",  "version": "1.0.0",  "type": "module"}

"type": "module" is the part that matters. ankipack ships as an ES module and the script below uses top-level await, and neither works without it.

Your folder should now look like this:

  • my-deck
    • package.json

2. Install ankipack

ankipack uses sql.js to build the collection database, so you install both:

bun add ankipack@^0.3 sql.jsbun add -d @types/sql.js
npm install ankipack@^0.3 sql.jsnpm install --save-dev @types/sql.js
pnpm add ankipack@^0.3 sql.jspnpm add -D @types/sql.js
yarn add ankipack@^0.3 sql.jsyarn add -D @types/sql.js

3. Write the script

Create deck.ts and build it up a piece at a time. The whole file is at the bottom if you would rather copy it in one go.

1

Start with the imports and sql.js. Everything else needs them:

deck.ts
ts
import initSqlJs from "sql.js";import { Deck, Note, Notetype, Package } from "ankipack";const SQL = await initSqlJs();
2

Choose a note type. A note type decides what fields a note has and what cards it produces. Notetype.basic() is Anki's Front and Back, one card per note:

const my_notetype = Notetype.basic();

The variable is yours to name. ankipack does not expect it to be called anything in particular.

3

Create a deck to put the notes in. description is optional and shows up on the deck's own screen, after you click into it:

const my_deck = new Deck({  name: "{{ deck }}",  description: "{{ about }}",});
4

Now the cards. fields is positional: the first value is Front and the second is Back, because that is the order Notetype.basic() defines them in. Listing them and looping is less to type than three separate calls, and it is how you would build a real deck from a spreadsheet or a database:

const cards = [  ["{{ front1 }}", "{{ back1 }}"], // Front, Back  ["{{ front2 }}", "{{ back2 }}"],  ["{{ front3 }}", "{{ back3 }}"],];for (const [front, back] of cards) {  my_deck.addNote(new Note({ notetype: my_notetype, fields: [front, back] }));}
5

Finally, put the deck in a package and write it out:

const pkg = new Package();pkg.addDeck(my_deck);await pkg.writeToFile("deck.apkg", SQL);console.log("wrote deck.apkg");
The whole file
deck.ts
ts
import initSqlJs from "sql.js";import { Deck, Note, Notetype, Package } from "ankipack";const SQL = await initSqlJs();const my_notetype = Notetype.basic();const my_deck = new Deck({  name: "{{ deck }}",  description: "{{ about }}",});const cards = [  ["{{ front1 }}", "{{ back1 }}"], // Front, Back  ["{{ front2 }}", "{{ back2 }}"],  ["{{ front3 }}", "{{ back3 }}"],];for (const [front, back] of cards) {  my_deck.addNote(new Note({ notetype: my_notetype, fields: [front, back] }));}const pkg = new Package();pkg.addDeck(my_deck);await pkg.writeToFile("deck.apkg", SQL);console.log("wrote deck.apkg");

4. Run it

Bun and Node both run TypeScript directly, so there is nothing to compile.

bun deck.ts
node deck.ts
node deck.ts
node deck.ts
Expected Output
wrote deck.apkg

Node only runs TypeScript unaided from 22.18. On 22.17 or older, either use a runner such as tsx, or rename the script to deck.js and change nothing else: the code above is valid JavaScript as written.

Your folder should now look like this:

  • my-deck
    • deck.ts
    • deck.apkg# the deck you just built
    • package.json

5. Import it into Anki

Open Anki, choose File then Import, and pick deck.apkg. You get a deck called {{ deck }} with three cards in it, ready to study.

Where to go next

You now have the shape of every deck you will build: a note type, a deck, some notes, a package.

Ship updates without duplicates

Read this before you publish anything. A rebuilt deck duplicates every note unless you pin its identities.

Build a custom note type

Your own fields and card templates, instead of Front and Back.

Attach media

Images and audio, and the filename rules that will otherwise cost you the whole import.

Set the scheduler

FSRS settings and deck options, and whether they reach your users at all.

Did this work on your setup?

Not rated yet