semantic-react-cli
September 14, 2026 ยท View on GitHub
A small CLI that scaffolds React components, helpers, hooks, contexts, Redux Toolkit slices, and Next.js pages.
Install
Global:
npm install -g semantic-react-cli
Or run without installing:
npx semantic-react-cli generate component Button --jsx
Usage
semantic-react generate component <Name> [--js | --jsx | --ts | --tsx]
You can also generate a helper file:
semantic-react generate helper <name> [--js | --jsx | --ts | --tsx]
Or a hook file:
semantic-react generate hook <name> [--js | --jsx | --ts | --tsx]
A React context, complete with a provider and a consumer hook:
semantic-react generate context <Name> [--js | --jsx | --ts | --tsx]
A Redux Toolkit slice, optionally scaffolding named reducer functions:
semantic-react generate slice <name> [reducers...] [--js | --jsx | --ts | --tsx]
Or a Next.js page (Pages Router by default, App Router with --app):
semantic-react generate page <name> [--app] [--js | --jsx | --ts | --tsx]
Delete a scaffold you no longer need:
semantic-react delete component <name> [--jsx | --ts | --tsx]
semantic-react delete helper <name> [--jsx | --ts | --tsx]
semantic-react delete hook <name> [--jsx | --ts | --tsx]
Rename a scaffold. It keeps the file's current extension unless you pass one, and asks whether to rename the exported function too:
semantic-react rename component <name> <newName> [--js | --jsx | --ts | --tsx]
semantic-react rename helper <name> <newName> [--js | --jsx | --ts | --tsx]
semantic-react rename hook <name> <newName> [--js | --jsx | --ts | --tsx]
semantic-react rename path <file> <newName> [--js | --jsx | --ts | --tsx]
Move a file to another path (creates the destination folder if it is missing):
semantic-react move components/Button.jsx ui/Button.jsx
List what is already scaffolded in the components, helpers, and hooks paths (or pass a directory to list its tree instead):
semantic-react list
semantic-react list src/features
To configure where those files land, create a settings file (see Custom output paths):
semantic-react init
generate is aliased to g, delete to d, rename to r, move to m,
list to l, component to c, helper to h, hook to k, context to
x, slice to s, and page to p, so this also works:
semantic-react g c Button --tsx
semantic-react g h formatDate --ts
semantic-react g k useToggle --tsx
semantic-react g x Theme --tsx
semantic-react g s counter increment decrement --ts
semantic-react g p about --tsx
semantic-react d c Button --tsx
What it creates
Running semantic-react g c Button --jsx in your project root creates:
components/
Button/
Button.jsx
Button.css
Button.jsx:
export default function Button() {
// ...
}
Running semantic-react g h formatDate --jsx creates:
helpers/
formatDate.jsx
Running semantic-react g k useToggle --tsx creates:
hooks/
useToggle.tsx
Helper and hook files share the same scaffold as components:
export default function formatDate() {
// ...
}
Running semantic-react g x Theme --tsx creates:
contexts/
Theme.tsx
import { createContext, createElement, useContext, useState } from 'react';
const ThemeContext = createContext(undefined);
export function ThemeProvider({ children }) {
const [value, setValue] = useState(null);
return createElement(ThemeContext.Provider, { value: { value, setValue } }, children);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
Running semantic-react g s counter increment decrement --ts creates:
slices/
counter.ts
import { createSlice } from '@reduxjs/toolkit';
const initialState = {};
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
// ...
},
decrement(state) {
// ...
},
},
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
Reducer names are optional; without them you get an empty reducers: {} block
and no actions export.
Running semantic-react g p about --tsx creates a Pages Router file:
pages/
about.tsx
export default function about() {
// ...
}
Pass --app for the App Router convention instead โ a route-segment folder
containing page.<ext>:
app/
about/
page.tsx
export default function Page() {
// ...
}
If a file with the same name already exists with a different extension, it is removed so each scaffold has a single source file.
Deleting scaffolds
Remove a scaffold with delete (aliased to d):
semantic-react delete component Button --jsx
semantic-react delete helper formatDate --ts
semantic-react delete hook useToggle --tsx
The extension flag selects which file to target, mirroring generate. With no
flag, the .js file is targeted:
semantic-react delete helper formatDate
delete componentremoves the component's source file, its colocated.cssfile, and the component folder itself once nothing else is left in it. If you keep other files (tests, stories) in that folder, the folder stays.delete helperanddelete hookremove the single source file.- Custom output paths from
semantic-react.settings.jsonare respected, sodeletelooks in the same placegeneratewrites. deleteandrenamedo not yet supportcontext,slice, orpagescaffolds; remove or rename those files by hand for now.- If the target file does not exist, the command prints a notice and exits without error.
Deletion is permanent. Files are removed from disk, not moved to the system trash. Commit your work (or rely on your editor's local history) if you want a way back.
Renaming scaffolds
Rename a scaffold with rename (aliased to r):
semantic-react rename component Button PrimaryButton
semantic-react rename hook useToggle useSwitch
semantic-react rename component Button PrimaryButton
Also rename the component function from Button to PrimaryButton? (y/N) y
Renamed component:
๐ข Button.jsx โ PrimaryButton.jsx
๐ฃ Button.css โ PrimaryButton.css
โ๏ธ function Button โ PrimaryButton
- The current extension is kept. Pass
--js,--jsx,--ts, or--tsxto switch it while renaming. rename componentmoves the source file, its colocated.cssfile, and the component folder; the old folder is removed once nothing else is left in it.- You are asked whether to rename the exported function. When confirmed, every
whole-word occurrence of the old name in the file is replaced (function
declaration, default export,
displayName, ...). - Custom output paths from
semantic-react.settings.jsonare respected. - Both names must stay inside the current project; a name that points outside
it (
../, an absolute path) is rejected. - If the source does not exist, or the target name is already taken, the command prints a notice and makes no changes.
Renaming a file by path
rename component/helper/hook look in the configured scaffold paths. To rename
a file that lives somewhere else โ for example one that move relocated โ
point rename path straight at it:
semantic-react rename path ui/Button.jsx PrimaryButton
- The file stays in its current folder; only the name changes. Any directory
or extension you include in
<newName>is ignored โ pass--js/--jsx/--ts/--tsxto switch the extension. - A colocated
.cssfile is renamed to match. - You are asked whether to rename the exported function, same as above.
- The path must be inside the current project.
Moving files
Move any file to another path with move (aliased to m):
semantic-react move components/Button.jsx ui/Button.jsx
- Both paths are taken as given, resolved from the directory you run the command in.
- Both paths must stay inside the current project. A path that points outside
it (
../, an absolute path elsewhere) is rejected. - If the source does not exist, the command prints a notice and does nothing.
- The destination folder is created if it is missing.
- If the destination is an existing directory (or ends with
/), the file is moved into it under its current name. - If a file already exists at the destination, you are asked before it is overwritten.
- If a path is missing its file extension, you are prompted for it; a destination that omits the extension reuses the source's.
- If the source has a colocated
.cssfile next to it (a component), it moves too, landing beside the moved file with the matching name (ui/Button.jsxโui/Button.css) so itsimport './...css'keeps working. If a file already sits at the CSS target, the CSS is left in place and the command says so. - If moving the file empties its folder (the
components/Button/case), the empty folder is removed. movenever changes the file's contents. If you also want the exported function renamed after a move, runsemantic-react rename pathon the moved file.
Listing scaffolds
See everything currently in your components, helpers, and hooks paths with
list (aliased to l):
semantic-react list
components (components)
โโโ ๐ Button
โ โโโ ๐จ Button.css
โ โโโ โ๏ธ Button.jsx
โโโ ๐ Nav
โโโ ๐ Menu
โ โโโ โ๏ธ Menu.jsx
โโโ โ๏ธ Nav.jsx
helpers (helpers)
โโโ ๐จ formatDate.js
hooks (hooks)
โโโ ๐ฆ useToggle.ts
- Each section header shows the kind and the configured path.
- Entries print as a tree with connecting lines; folders (๐) are expanded recursively so you can see nested folders and the colocated files inside.
- Files are labelled with a language icon (โ๏ธ React
.jsx/.tsx, ๐ฆ TypeScript.ts, ๐จ JavaScript.js, ๐จ CSS). - Custom output paths from
semantic-react.settings.jsonare respected. - A path that does not exist yet or is empty is reported as such instead of
erroring; an empty folder shows
โโโ (empty). - Pass a directory (
semantic-react list src/features) to print the tree under that path instead of the configured scaffold paths.
Custom output paths
By default, scaffolds are written to components/, helpers/, hooks/,
contexts/, slices/, pages/, and app/ (for --app pages) in your
project root. To send them somewhere else, add a semantic-react.settings.json
file to your project root.
Run init to drop one in, pre-filled with the default paths:
semantic-react init
{
"helpers": "helpers",
"hooks": "hooks",
"components": "components",
"contexts": "contexts",
"slices": "slices",
"pages": "pages",
"appPages": "app"
}
init will not clobber an existing file; pass --force to overwrite it.
Then edit the paths to taste, for example:
{
"components": "src/components",
"helpers": "src/lib/helpers",
"hooks": "src/hooks",
"contexts": "src/contexts",
"slices": "src/store/slices",
"pages": "src/pages",
"appPages": "src/app"
}
With that file in place, semantic-react g c Button --jsx creates:
src/components/
Button/
Button.jsx
Button.css
Notes:
- Every key is optional. Any key you leave out falls back to its default
(
components,helpers,hooks,contexts,slices,pages,appPages). pagesapplies togenerate pagewithout--app;appPagesapplies togenerate page --app.- If
semantic-react.settings.jsonis missing entirely, the defaults are used, so existing projects need no changes. - Paths are resolved relative to the directory you run the command from
(your project root) and nested paths like
src/componentsare created automatically. - Unknown keys in the file are ignored.
Options
| Flag | Output extension |
|---|---|
| (none) | .js |
--jsx | .jsx |
--ts | .ts |
--tsx | .tsx |
License
ISC