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 component removes the component's source file, its colocated .css file, 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 helper and delete hook remove the single source file.
  • Custom output paths from semantic-react.settings.json are respected, so delete looks in the same place generate writes.
  • delete and rename do not yet support context, slice, or page scaffolds; 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 --tsx to switch it while renaming.
  • rename component moves the source file, its colocated .css file, 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.json are 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/--tsx to switch the extension.
  • A colocated .css file 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 .css file next to it (a component), it moves too, landing beside the moved file with the matching name (ui/Button.jsx โ†’ ui/Button.css) so its import './...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.
  • move never changes the file's contents. If you also want the exported function renamed after a move, run semantic-react rename path on 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.json are 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).
  • pages applies to generate page without --app; appPages applies to generate page --app.
  • If semantic-react.settings.json is 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/components are created automatically.
  • Unknown keys in the file are ignored.

Options

FlagOutput extension
(none).js
--jsx.jsx
--ts.ts
--tsx.tsx

License

ISC