Skip to content

Build a Worksheet

This tutorial walks you through building a Node.js script that generates a printable math worksheet using the StudyPlug API. By the end, you will have a standalone HTML file with 10 multiplication problems and an answer key.

  • Node.js 18+
  • A terminal

Create a new project and install the TypeScript SDK.

Terminal window
mkdir math-worksheet && cd math-worksheet
npm init -y
npm install studyplug

Before generating content, find the right skill slug. Let’s look for Grade 3 multiplication skills.

browse.ts
import { StudyPlug } from "studyplug";
const sp = new StudyPlug();
const { data } = await sp.skills.list({
grade: "grade-3",
subject: "math",
});
for (const skill of data.skills) {
console.log(`${skill.slug}${skill.name}`);
}

Run it:

Terminal window
npx tsx browse.ts

You will see skills like multiply-by-3, multiply-by-6, and others. We will use multiply-by-6 for this worksheet.

Now generate a batch of multiplication problems. Pass a seed so the worksheet is reproducible.

generate.ts
import { StudyPlug } from "studyplug";
const sp = new StudyPlug();
const { data } = await sp.generate({
skill: "multiply-by-6",
grade: "grade-3",
count: 10,
seed: 101,
});
console.log(`Generated ${data.items.length} problems`);
console.log(`Answer key has ${data.answerKey.length} entries`);

Each item in data.items is a ContentItem with structured fields like content.operand1, content.operand2, content.operator, and a corresponding entry in data.answerKey.

Build a simple HTML template that renders the problems and looks clean when printed.

worksheet.ts
import { StudyPlug } from "studyplug";
import { writeFileSync } from "node:fs";
const sp = new StudyPlug();
const { data } = await sp.generate({
skill: "multiply-by-6",
grade: "grade-3",
count: 10,
seed: 101,
});
const problemRows = data.items
.map((item, i) => {
const c = item.content;
if (c.type !== "arithmetic") return "";
const num = i + 1;
return `<tr>
<td>${num}.</td>
<td>${c.operand1} ${c.operator} ${c.operand2} = ______</td>
</tr>`;
})
.join("\n");
const answerRows = data.answerKey
.map((entry, i) => {
return `<li>${i + 1}. ${entry.answer}</li>`;
})
.join("\n");
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Multiplication Worksheet — Grade 3</title>
<style>
body { font-family: Arial, sans-serif; max-width: 700px; margin: 40px auto; }
h1 { text-align: center; font-size: 24px; }
table { width: 100%; border-collapse: collapse; margin: 24px 0; }
td { padding: 12px 8px; font-size: 20px; }
.answer-key { margin-top: 48px; border-top: 2px dashed #ccc; padding-top: 16px; }
.answer-key h2 { font-size: 16px; color: #666; }
.answer-key li { font-size: 14px; color: #666; }
@media print {
.answer-key { page-break-before: always; }
}
</style>
</head>
<body>
<h1>Multiplication Practice</h1>
<p>Name: _________________________ &nbsp; Date: _____________</p>
<table>${problemRows}</table>
<div class="answer-key">
<h2>Answer Key</h2>
<ol>${answerRows}</ol>
</div>
</body>
</html>`;
writeFileSync("worksheet.html", html);
console.log("Wrote worksheet.html — open it in a browser and print!");
Terminal window
npx tsx worksheet.ts

Open worksheet.html in your browser and hit Ctrl+P (or Cmd+P) to print. The answer key appears on a separate page thanks to the page-break-before CSS rule.

  • Browsed the StudyPlug catalog to find a skill slug
  • Generated 10 deterministic multiplication problems with a seed
  • Rendered them as a printable HTML worksheet with an answer key
  • Change the seed to get a different set of problems with the same skill
  • Swap multiply-by-6 for any skill slug from the catalog
  • Increase count up to 50 for longer worksheets
  • Use renderHints.print.columns from each item to arrange problems in a two-column layout

If you are building with Next.js 14+ (App Router), here is how to integrate the API into a Server Component and Route Handler.

Create app/api/worksheet/route.ts to proxy requests through your backend:

app/api/worksheet/route.ts
import { StudyPlug } from "studyplug";
import { NextResponse } from "next/server";
const sp = new StudyPlug({
apiKey: process.env.STUDYPLUG_API_KEY, // optional, for higher rate limits
});
export async function POST(request: Request) {
const { skill, grade, count, seed } = await request.json();
const { data } = await sp.generate({
skill,
grade,
count: count ?? 10,
seed,
});
return NextResponse.json(data);
}

Fetch skills and generate content directly in a Server Component:

app/worksheet/page.tsx
import { StudyPlug } from "studyplug";
const sp = new StudyPlug();
export default async function WorksheetPage() {
const { data } = await sp.generate({
skill: "multiply-by-6",
grade: "grade-3",
count: 10,
seed: 101,
});
return (
<main>
<h1>Multiplication Practice</h1>
<ol>
{data.items.map((item, i) => {
if (item.content.type !== "arithmetic") return null;
const c = item.content;
return (
<li key={i}>
{c.operand1} {c.operator} {c.operand2} = ______
</li>
);
})}
</ol>
</main>
);
}