Custom Generators
Create your own generators to produce any file structure you need. This goes beyond the default feature generator — you define exactly what files to create and where.
Why custom generators?
The built-in feature generator works great for the standard stack (React + TanStack Query + Zustand + Zod). But every project has unique needs:
- You use a different state management library
- You need additional file types (tests, stories, CSS modules)
- Your folder structure follows a different convention
- You want to generate API routes, not just components
Custom generators solve all of these.
Generator anatomy
A generator is a named set of file definitions. Each file definition has:
- A template (Handlebars file with variables)
- An output path (where to write the result)
{
"generators": {
"my-generator": {
"files": [
{
"template": "path/to/template.hbs",
"outputPath": "{{PascalName}}File.ts"
}
]
}
}
}Example 1: Component-only generator
If you only need components without the full feature structure:
{
"generators": {
"component": {
"files": [
{ "template": "component/Component.tsx.hbs", "outputPath": "{{PascalName}}.tsx" },
{ "template": "component/styles.css.hbs", "outputPath": "{{PascalName}}.css" },
{ "template": "component/test.tsx.hbs", "outputPath": "{{PascalName}}.test.tsx" }
]
}
}
}Usage:
npx heco generate my-button --generator componentResult:
my-button/
├── MyButton.tsx
├── MyButton.css
└── MyButton.test.tsxExample 2: API route generator
Generate Next.js API routes with handler, validation, and types:
{
"generators": {
"api-route": {
"files": [
{ "template": "api/route.ts.hbs", "outputPath": "route.ts" },
{ "template": "api/schema.ts.hbs", "outputPath": "{{camelName}}Schema.ts" },
{ "template": "api/types.ts.hbs", "outputPath": "{{camelName}}Types.ts" }
]
}
}
}Usage:
npx heco generate create-user --generator api-routeResult:
create-user/
├── route.ts
├── createUserSchema.ts
└── createUserTypes.tsExample 3: Full Next.js page generator
Generate a page with all its supporting files: the page itself, data fetching hooks, components, types, and styles:
{
"generators": {
"page": {
"files": [
{ "template": "next/page.tsx.hbs", "outputPath": "page.tsx" },
{ "template": "next/layout.tsx.hbs", "outputPath": "layout.tsx" },
{ "template": "components/Component.tsx.hbs", "outputPath": "_components/{{PascalName}}View.tsx" },
{ "template": "hooks/useData.ts.hbs", "outputPath": "_hooks/use{{PascalName}}Data.ts" },
{ "template": "types/types.ts.hbs", "outputPath": "_types/{{PascalName}}Types.ts" }
]
}
}
}Example 4: Test file generator
Generate test files for existing features:
{
"generators": {
"test": {
"files": [
{ "template": "test/component.test.tsx.hbs", "outputPath": "{{PascalName}}.test.tsx" },
{ "template": "test/hook.test.ts.hbs", "outputPath": "use{{PascalName}}.test.ts" },
{ "template": "test/e2e.spec.ts.hbs", "outputPath": "{{camelName}}.e2e.spec.ts" }
]
}
}
}Registering generators programmatically
You can also register generators in your Node.js code:
import { registerGenerator } from "@heco-gen/features"
registerGenerator("custom-route", {
files: [
{
template: "custom/route.ts.hbs",
outputPath: "routes/{{camelName}}.ts",
},
],
})This is useful when building CLI tools, scaffolding scripts, or integration with your own tooling.
Generator execution order
When you run npx heco generate <name>, the CLI:
- Looks for a generator matching
<name>in your config generators - Falls back to the default
featuregenerator if no match - Processes each file definition in order
- For each file, resolves the template, renders it, and writes the result
Files are processed in the order they appear in the files array. This matters when one file depends on another being created first (though usually all files are independent).
File output paths
Output paths are relative to the feature directory (<basePath>/<feature-name>/). You can use all naming variables:
| Variable | Resolves to |
|---|---|
{{PascalName}} | UserProfile |
{{camelName}} | userProfile |
{{snakeName}} | user_profile |
{{upperName}} | USER_PROFILE |
{{name}} | user-profile |
You can also create nested directories:
{ "outputPath": "deeply/nested/{{PascalName}}Component.tsx" }Passing the generator flag
npx heco generate blog --generator pageIf you omit --generator, the default feature generator is used.
Next steps
- Templates — learn how to create dynamic templates with variables, conditionals, and loops
- Programmatic Usage — use generators from your own code