Note: SDK packages will be published at launch. This tutorial previews the API.
Build a Budget Tracker
Build a complete budget tracking application using Mog and React. You will set up a spreadsheet with budget categories and formulas, add conditional formatting to highlight over-budget items, create a summary chart, and export everything to XLSX.
Prerequisites
- •Node.js 18+ and a React project (Next.js, Vite, or CRA)
- •Completed the Embed a Spreadsheet in Next.js tutorial (recommended)
npm install @mog-sdk/embed @mog-sdk/node1Set up the spreadsheet with categories and formulas
Create a helper that initializes the budget spreadsheet with categories, budget amounts, and formulas that calculate actual spend and variance.
// lib/budget-setup.ts
import { createWorkbook, type Workbook } from "@mog-sdk/node";
export interface BudgetCategory {
name: string;
budget: number;
}
const DEFAULT_CATEGORIES: BudgetCategory[] = [
{ name: "Housing", budget: 1500 },
{ name: "Food & Groceries", budget: 600 },
{ name: "Transportation", budget: 300 },
{ name: "Utilities", budget: 200 },
{ name: "Entertainment", budget: 150 },
{ name: "Healthcare", budget: 100 },
{ name: "Savings", budget: 500 },
];
export async function createBudgetWorkbook(
categories = DEFAULT_CATEGORIES
): Promise<Workbook> {
const wb = await createWorkbook();
const ws = wb.getActiveSheet();
ws.name = "Budget";
// Headers
const headers = ["Category", "Budget", "Actual", "Variance", "Status"];
for (let col = 0; col < headers.length; col++) {
await ws.setCellByIndex(0, col, headers[col]);
}
// Data rows with formulas
for (let i = 0; i < categories.length; i++) {
const row = i + 2; // 1-indexed, skip header
await ws.setCell(`A${row}`, categories[i].name);
await ws.setCell(`B${row}`, categories[i].budget);
await ws.setCell(`C${row}`, 0); // Actual — user fills this in
await ws.setCell(`D${row}`, `=B${row}-C${row}`);
await ws.setCell(`E${row}`, `=IF(D${row}<0,"Over Budget","On Track")`);
}
// Totals row
const lastRow = categories.length + 1;
const totalRow = lastRow + 1;
await ws.setCell(`A${totalRow}`, "TOTAL");
await ws.setCell(`B${totalRow}`, `=SUM(B2:B${lastRow})`);
await ws.setCell(`C${totalRow}`, `=SUM(C2:C${lastRow})`);
await ws.setCell(`D${totalRow}`, `=SUM(D2:D${lastRow})`);
await ws.setCell(
`E${totalRow}`,
`=IF(D${totalRow}<0,"Over Budget","On Track")`
);
await wb.calculate();
return wb;
}2Add conditional formatting
Highlight over-budget items in red and on-track items in green using Mog's conditional formatting API.
// lib/budget-formatting.ts
import type { Workbook } from "@mog-sdk/node";
export async function applyBudgetFormatting(wb: Workbook) {
const ws = wb.getActiveSheet();
const lastDataRow = (await ws.getUsedRange()).length;
// Red background for negative variance (over budget)
await ws.addConditionalFormat({
range: `D2:D${lastDataRow}`,
type: "cellValue",
operator: "lessThan",
value: 0,
style: {
background: "#fecaca",
color: "#991b1b",
bold: true,
},
});
// Green background for positive variance (on track)
await ws.addConditionalFormat({
range: `D2:D${lastDataRow}`,
type: "cellValue",
operator: "greaterThanOrEqual",
value: 0,
style: {
background: "#bbf7d0",
color: "#166534",
},
});
// Bold the header row
await ws.setRowStyle(0, {
bold: true,
background: "#1e293b",
color: "#f8fafc",
});
// Bold the totals row
await ws.setRowStyle(lastDataRow - 1, { bold: true });
}3Create a summary chart
Add a bar chart that compares budget vs. actual spend for each category.
// lib/budget-chart.ts
import type { Workbook } from "@mog-sdk/node";
export async function addBudgetChart(wb: Workbook) {
const ws = wb.getActiveSheet();
const categoryCount = (await ws.getUsedRange()).length - 2; // minus header and total
const chart = await ws.addChart({
type: "bar",
title: "Budget vs. Actual",
position: { sheet: ws.name, cell: "G2", width: 500, height: 350 },
series: [
{
name: "Budget",
categories: `A2:A${categoryCount + 1}`,
values: `B2:B${categoryCount + 1}`,
color: "#6366f1",
},
{
name: "Actual",
categories: `A2:A${categoryCount + 1}`,
values: `C2:C${categoryCount + 1}`,
color: "#f97316",
},
],
legend: { position: "bottom" },
});
return chart;
}4Wire it up in React with XLSX export
Bring everything together in a React component. The user can edit actual spend values, see live variance calculations, and export to XLSX.
"use client";
import { MogSheet } from "@mog-sdk/embed/react";
import type { CellChangeEvent } from "@mog-sdk/embed";
import { useCallback, useRef, useState } from "react";
export default function BudgetTracker() {
const sheetRef = useRef<any>(null);
const [totalVariance, setTotalVariance] = useState<number | null>(null);
const handleReady = useCallback(async (api: any) => {
sheetRef.current = api;
// Read the initial total variance
const variance = await api.getCellValue("D9");
setTotalVariance(variance as number);
}, []);
const handleCellChange = useCallback(async (e: CellChangeEvent) => {
// When actual spend changes, re-read the total variance
if (e.ref.startsWith("C")) {
const api = sheetRef.current;
if (api) {
const variance = await api.getCellValue("D9");
setTotalVariance(variance as number);
}
}
}, []);
const handleExport = useCallback(async () => {
const api = sheetRef.current;
if (!api) return;
const blob = await api.exportXlsx();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "budget-tracker.xlsx";
a.click();
URL.revokeObjectURL(url);
}, []);
return (
<div className="mx-auto max-w-5xl p-8">
<div className="mb-6 flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">Budget Tracker</h1>
{totalVariance !== null && (
<p
className={`mt-1 text-sm font-medium ${
totalVariance >= 0 ? "text-green-600" : "text-red-600"
}`}
>
Total variance: ${totalVariance >= 0 ? "+" : ""}
{totalVariance.toLocaleString()}
</p>
)}
</div>
<button
onClick={handleExport}
className="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-500"
>
Export XLSX
</button>
</div>
<MogSheet
width={1100}
height={500}
onReady={handleReady}
onCellChange={handleCellChange}
toolbar={{
show: true,
items: ["bold", "italic", "separator", "align", "separator", "undo", "redo"],
}}
features={{
formulaBar: true,
sheetTabs: false,
contextMenu: true,
}}
/>
</div>
);
}To initialize the spreadsheet with budget data on mount, call the setup functions from Step 1 and Step 2 inside the onReady callback, or pre-generate an XLSX file and pass it as the src prop.
Next steps
Embed a Spreadsheet in Next.js
Learn the fundamentals of the MogSheet embed component.
Use Mog from Python
Generate budget reports with the Python SDK and pandas.
API Reference
Full reference for charts, conditional formatting, and styling APIs.
Process XLSX Files Server-Side
Pre-generate budget templates on the server with @mog-sdk/node.