A Spring Boot back end with a React front end, built by Gradle
This page describes one arrangement: a Spring Boot service that serves a React front end from its own static resources, with Gradle driving the whole build. The attraction is a single build command and a single deployable artefact; the cost is that the front end is coupled to the back end’s release cycle, which is the wrong trade for anything that needs to ship independently or sit behind a CDN.
Node’s package manager is used as the scaffolding tool for the React application.
npm create vite@latest generates a project directory that looks roughly like this:
my-app/ README.md node_modules/ index.html package.json vite.config.ts public/ favicon.ico src/ App.css App.tsx index.css main.tsxCreate the project:
mkdir my-react-projectcd my-react-projectgradle initnpm create vite@latest frontend -- --template react-tsOlder versions of this arrangement used npx create-react-app frontend. Create React App was
deprecated by the React team on 14 February 2025 and prints a deprecation notice when run; use a
build tool such as Vite, or one of the frameworks React now recommends. See
Creating a React app.
React components are written with JSX, a syntax extension to JavaScript that allows HTML-like
markup inside a JavaScript or TypeScript file. It is optional — the same components can be written
as plain React.createElement calls — but essentially every codebase uses it.
File extensions follow the toolchain: .jsx and .tsx mark files containing JSX, .js and .ts
those that do not. App.tsx holds the root component; main.tsx is the entry point that renders
it into the root element declared in index.html. See
Guide to file extensions in React projects
for the trade-offs between the four.
Build configuration
Section titled “Build configuration”Vite keeps its configuration visible in vite.config.ts, so customising the build — changing the
output directory, adding a proxy for the API during development, adding a plugin — is an ordinary
edit to a file in the repository.
This is the main practical difference from Create React App, which deliberately hid its Webpack and
Babel configuration inside the react-scripts package to keep the setup zero-configuration.
Customising a CRA build meant one of three things, all of which are still visible in older
projects: npm run eject, which copies the configuration into the project irreversibly; CRACO
(Create React App Configuration Override); or react-app-rewired.
For this arrangement the setting that matters is the build output directory, which must land where Gradle expects to collect it.
Gradle
Section titled “Gradle”JSX and TypeScript have to be compiled to JavaScript a browser runtime can execute. Rather than
invoking Node by hand, Gradle drives it, so gradle build produces the front end as a side effect
of building the service. The
gradle-node-plugin
installs a pinned Node version and exposes the npm scripts as Gradle tasks, which also keeps CI
from depending on whatever Node happens to be on the agent.
The compiled bundle is then copied into Spring Boot’s static resources:
/src/main/resources/static/Anything under that directory is served from the root of the application, so index.html becomes
the application’s home page and the hashed asset files are served alongside it.
Authenticating at the edge
Section titled “Authenticating at the edge”A common variation terminates authentication in front of the application rather than inside it: a reverse proxy validates the JWT and passes only authenticated requests through, so the service never sees an unauthenticated request.
If NGINX is the proxy, note that its auth_jwt directive comes from ngx_http_auth_jwt_module,
which is available only as part of the commercial
subscription — open-source NGINX has
no such directive. The open-source options are to script the validation with the njs or Lua module,
or to put an API gateway in front that does it natively.