Stop struggling with cumbersome theme.json files
This week, during WooCommerce Office Hours, I chatted with a community member about the challenges of managing large, cumbersome theme.json files—something I can definitely relate to. I'm currently working on a new WordPress block theme, and editing a 900+ line file has been a real struggle.
There’s no official Core solution, but I knew a compiler/bundler could make things much easier. This would allow me to break theme.json into smaller, manageable files. However, learning to build one felt too time-consuming and unnecessary given how few themes I create each year—until now. With AI tools like Cursor, time and expertise are no longer barriers.
While still chatting with the community member, I prompted Cursor for a solution, and within minutes, I had a working Node.js script. Since I’m already using Sass in the theme for custom block styles, integrating this script was quick and straightforward.
I was so impressed with how easy it was that I recorded this short video:
For those curious, here’s the script Cursor generated. However, I wouldn't recommend copy and pasting this code. Use AI to generate a custom solution tailored to your own workflow and theme structure.
const fs = require("fs");
const path = require("path");
// Base theme.json structure
const baseConfig = require("../src/json/base.json");
// Function to read and parse JSON file
function readJsonFile(filePath) {
try {
return JSON.parse(fs.readFileSync(filePath, "utf8"));
} catch (error) {
console.error(`Error reading ${filePath}:`, error);
return {};
}
}
// Enhanced deep merge function to handle arrays
function deepMerge(target, source) {
for (const key in source) {
if (source[key] instanceof Object) {
if (Array.isArray(source[key])) {
// If it's an array, replace it entirely
target[key] = source[key];
} else if (key in target) {
// If it's an object and exists in target, merge recursively
target[key] = deepMerge(target[key], source[key]);
} else {
// If it's an object but doesn't exist in target, copy it
target[key] = source[key];
}
} else {
// For primitive values, simply replace
target[key] = source[key];
}
}
return target;
}
// Function to read all JSON files in a directory
function readJsonDirectory(dirPath) {
let result = {};
try {
const files = fs.readdirSync(dirPath);
files.forEach((file) => {
if (file.endsWith(".json")) {
const filePath = path.join(dirPath, file);
console.log(`Processing: ${filePath}`);
const jsonData = readJsonFile(filePath);
result = deepMerge(result, jsonData);
}
});
} catch (error) {
console.error(`Error reading directory ${dirPath}:`, error);
}
return result;
}
// Build final theme.json
function buildThemeJson() {
try {
// Read settings
console.log("Processing settings...");
const settings = readJsonDirectory(
path.join(__dirname, "../src/json/settings"),
);
baseConfig.settings = settings;
// Read styles
console.log("Processing styles...");
const styles = readJsonDirectory(
path.join(__dirname, "../src/json/styles"),
);
baseConfig.styles = styles;
// Write final theme.json with tab indentation
const outputPath = path.join(__dirname, "../theme.json");
fs.writeFileSync(outputPath, JSON.stringify(baseConfig, null, "\t"));
console.log(`Successfully created theme.json at ${outputPath}`);
} catch (error) {
console.error("Error building theme.json:", error);
process.exit(1);
}
}
buildThemeJson();The AI also added a dedicated script to my theme's package.json file, which looks like this:
{
...
"scripts": {
...
"build:theme-json": "node scripts/build-theme-json.js"
},
...
}So, whenever I want to generate the complete theme.json file, I just run:
npm run build:theme-jsonEvery day, I discover new ways AI can streamline my workflow and save me time. If you haven’t tried these tools yet, I highly recommend starting with Cursor. I recently recorded a short primer for WordPress.com that should help you get started.