CodeVetter · Repo Unpack · Extension points
react/create-react-app
CRA's extension model is deliberate and narrow: custom templates, a replaceable react-scripts package (--scripts-version), env vars, package.json fields (proxy, homepage, browserslist, jest, babel), and finally eject. There is no in-process plugin registry. The practical plug-in points inside the repo are the webpack config factory, the Jest config factory, babel-preset-react-app's create() options, and ESLint's optional plugin detection. Community tools (CRACO, react-app-rewired) patch these from outside because the project refuses a first-party override API.
- Templates are the supported product extension. A template package must be named cra-template-* (or scoped) and contain template.json plus a template/ directory. template.json keys merged by init.js are package.json fields: dependencies, scripts, eslintConfig, babel, jest, browserslist. Any extra files under template/ are copied verbatim.
- --scripts-version replaces the entire toolchain package. A fork must provide bin/react-scripts.js and scripts/init.js, start.js, build.js, test.js. The CLI does not feature-detect; it only installs and executes init.js.
- webpack.config.js exports a factory function of webpackEnv. Internal extension is editing that factory (or ejecting a copy). There is no plugins array exposed to package.json. Advanced-configuration docs point users at environment variables rather than a config file.
- Environment extension surface (read by env.js and start.js): PORT, HOST, HTTPS, SSL_CRT_FILE, SSL_KEY_FILE, BROWSER, BROWSER_ARGS, PUBLIC_URL, BUILD_PATH, GENERATE_SOURCEMAP, INLINE_RUNTIME_CHUNK, IMAGE_INLINE_SIZE_LIMIT, FAST_REFRESH, CHOKIDAR_USEPOLLING, TSC_COMPILE_ON_ERROR, ESLINT_NO_DEV_ERRORS, DISABLE_ESLINT_PLUGIN, DISABLE_NEW_JSX_TRANSFORM, WDS_SOCKET_HOST, WDS_SOCKET_PATH, WDS_SOCKET_PORT, DANGEROUSLY_DISABLE_HOST_CHECK, CI, and REACT_APP_*.
- package.json fields honored without eject: proxy (string or object consumed by prepareProxy), homepage (feeds publicPath via paths.js), browserslist, and optional jest/babel overrides that get merged but are only partially respected — createJestConfig ignores most user keys unless they were written by eject. Users commonly discover this limitation.
- babel-preset-react-app's create(options) is the Babel extension point inside the monorepo. webpack and Jest both call it with absoluteRuntime, flow flags, and helpers. babel-plugin-named-asset-import is injected here so SVG ReactComponent imports work. Custom presets in the app are not loaded unless the user ejects or replaces the preset.
- ESLint extension is eslintConfig in package.json of the generated app, which extends react-app and optionally react-app/jest. Adding rules works; changing the parser for TS is automatic only when typescript and the parser package are installed. The config file exports a function so it can conditionally require plugins.
- Jest transformers are extension points for non-JS assets: cssTransform returns an empty module, fileTransform stringifies filenames (and special-cases SVG for ReactComponent). New asset types require a new transform plus a moduleNameMapper entry in createJestConfig.
- react-error-overlay is replaceable only by eject or by aliasing the module. It exposes a small runtime API (listen, report, dismiss, startReportingRuntimeErrors) used by webpackHotDevClient. Editing overlay UX means changing that package and rebuilding it (npm run build).
- Docusaurus docs extension is file-based: drop an md file in docusaurus/docs and add it to sidebars.json. swizzle script exists for theming. This does not affect the CLI.
- Supported alternatives to a plugin API, as documented, are: composing libraries inside src/, using a custom template, forking react-scripts, or ejecting. The docs page alternatives-to-ejecting.md is the product decision record: CRA will not grow an overrides config.