Build, Preview, and First Project Review
The milestone app works in Vite's development server. A deployment check must also resolve every production import, pass the TypeScript check, emit optimized client files, serve those files, and survive a clean page load.
Begin from a Known Project State
Save every source file and check the working directory.
pwd
npm runpwd should print the project root. npm run should list dev, build, lint, preview, and the typecheck script added in the TypeScript lesson.
Stop the development server if its terminal output is obscuring the commands. The build does not require the dev server to be running.
You can run a build in another terminal while Vite development continues. Stopping it here only makes the sequence easier to observe.
Run Static Checks Before Packaging
Start with linting.
npm run lintThen run TypeScript.
npm run typecheckThese tools inspect different facts. ESLint applies configured rules to source. TypeScript verifies static type relationships. Neither opens the app and clicks its controls.
A lint failure should remain a failure. Do not add a file-wide disable comment merely to make the command green.
/* eslint-disable */Repair the reported code or narrow a rule suppression to a documented case. A review command that succeeds by ignoring the whole file has stopped protecting the project.
Likewise, do not replace a useful TypeScript error with any until the intended data contract is understood.
Build the Production Output
Run the build script.
npm run buildFor the TypeScript project, the script should include both the compiler check and Vite build.
"build": "tsc -b && vite build"The left command must succeed before the right command starts. This prevents an output directory from being presented as approved when the source has type errors.
Vite transforms the production module graph, resolves imports, processes assets, splits and combines output where configured, minifies production code, and writes the result to dist by default.
Typical output resembles this.
dist/
assets/
index-C8d3f1.js
index-A2e9c4.css
index.html
react-mark.svgThe content hashes in filenames can change whenever file content changes. Do not hardcode a generated asset filename into source or deployment scripts.
dist is generated output. Edit src, configuration, or public assets and rebuild. Do not repair a deployed application by hand-editing a hashed file in dist.
Read the Build Report
Vite prints the emitted files and their sizes. The report can reveal an unexpectedly large JavaScript chunk, but compressed size alone does not explain the cause.
If an early milestone with local data emits a huge bundle, inspect new dependencies and imports.
npm ls --depth=0A package can be installed without reaching the bundle, and a small direct package can import a large graph. Use a bundle analysis plugin only when the size needs investigation. Do not add analysis tooling permanently to solve a one-time question without reviewing its maintenance cost.
Record the output and investigate any unexpectedly large asset.
The Build Is Not a Server
The dist directory contains files. The browser still needs an HTTP server to request them.
Vite supplies a local preview command.
npm run previewOpen the URL printed by the terminal. It normally uses a different port from the development server.
Preview serves the built files. It does not recreate development module transformation, the error overlay, or Fast Refresh. Source edits do not change the preview until another build is produced.
edit source
-> run build again
-> restart or reload preview as needed
-> inspect new outputVite preview is for local inspection of a build. It is not a production server with the operational policy your deployment may require.
Test the Production Page from a Clean Load
Open preview in a fresh private window or clear the current page state, then load the URL directly.
Check the milestone interactions.
- The page displays without a console error.
- Lesson search responds to text input.
- The completed filter changes the visible list.
- The weekly goal accepts valid editing states.
- Reset returns the controls to their initial values.
- Buttons can be reached and activated with the keyboard.
- Labels activate or focus their controls.
The private window is not a complete cache test. It is a convenient way to remove preserved component state and most extension influence from the first load.
Use the Network panel and reload. Confirm that index.html, JavaScript, CSS, and image requests succeed. A blank page with a successful index.html can still have a failed JavaScript asset request.
Check Asset Base Paths
An application deployed at the origin root can use the default Vite base.
https://example.com/An application deployed beneath a path may require a configured base.
https://example.com/reactbook/Vite rewrites imported production asset URLs according to the base configuration. Public URLs written as absolute root paths still begin at the origin root.
<img src="/react-mark.svg" alt="" />That path requests https://example.com/react-mark.svg, not /reactbook/react-mark.svg. Test the actual intended hosting path before release.
For this local milestone, the app is served at /, so the default is sufficient.
Client Routes Need Server Fallback
The current app has one page and needs no client router. A multi-page single-page application may use browser URLs such as /lessons/state.
The hosting server must then return the app's index.html for recognized client routes. Otherwise, clicking inside the app can work while loading /lessons/state directly returns a server 404.
This is a deployment rule, not a React component rule. Frameworks with server routes handle it through their own server or adapter. Static SPA hosting uses a rewrite or fallback policy.
Do not add that policy before routes exist. When direct URL loads are introduced, verify the hosting-server configuration.
Client Environment Values Are Build Inputs
Vite replaces client environment references during the build for the selected mode.
const apiOrigin = import.meta.env.VITE_API_ORIGIN;Changing the deployment environment after files are built does not necessarily rewrite JavaScript already emitted to dist. A platform that needs runtime configuration must supply a separate runtime mechanism, such as a server-generated configuration response or HTML value.
This milestone has no API origin and no secret. Keep the build free of invented environment settings.
Inspect the output before deploying. Client bundles are public, including client environment values compiled into them.
Run a Clean Installation Check
The project may work because the current node_modules contains an old or undeclared package. A clean installation tests the recorded dependency state.
Commit or copy the current work before removing installed output. Then run the repeatable installation in a clean checkout or automated environment.
npm ci
npm run lint
npm run typecheck
npm run buildnpm ci requires the lockfile to agree with package.json. If this sequence fails after local npm install succeeded, investigate the recorded files rather than copying the old node_modules directory.
In a shared project, run these commands in continuous integration as well as locally.
Review the Component Design
The application is small enough to read from top to bottom. Do not create abstractions without a concrete repeated need.
Components
- Component names begin with uppercase letters.
- Component functions live at module level.
- Components have one clear UI responsibility.
- Repeated lesson markup has stable keys from lesson IDs.
- A wrapper element exists only when the DOM needs it.
Props
- Prop types state true requirements.
- Optional props have defined omitted behavior.
- Children are used for content composition, not hidden configuration.
- Callback props describe component intent, such as
onOpen(lessonId). - Props and nested prop objects are not mutated during render.
State
- State stores query text, filter choice, and goal input because users can change them independently.
- Filtered lessons and counts are derived during render.
- State updates use updater functions when they depend on previous state.
- The Reset button changes the stored state in one event.
- No Effect was added for a calculation.
HTML and interaction
- Actions use buttons and navigation uses links.
- Inputs have labels.
- Checkbox state uses
checked. - Buttons inside forms declare the intended type.
- Visible focus styles remain available.
Project records
package.jsonscripts match the commands used in review.- The lockfile is committed.
node_modulesanddistare not treated as hand-written source.- React and React DOM use compatible versions.
- Vite and Node.js meet the current documented version requirements.
A review checklist should point to observable code and behavior. "Looks clean" cannot be reproduced by the next reviewer.
Review the Failure Paths
Success is only one state. Reproduce a few safe failures before calling the milestone complete.
Change an import path and confirm the terminal identifies the importing file. Restore it.
Pass a wrong prop type and confirm npm run typecheck rejects it. Restore it.
Remove a list key and confirm the React development warning identifies the list. Restore it.
Set the filter to a query with no match and confirm the app explains the empty result rather than showing unexplained blank space.
These checks exercise the available feedback systems. They are not a substitute for automated tests in a production application.
The First Milestone
The finished application has one client root, a component tree, typed props, local state, controlled inputs, a derived list, and a reset interaction. Its development server updates source quickly, and its production command emits files that can be previewed from a clean load.
browser document
-> Vite-built client entry
-> React root
-> component tree
-> props and local state
-> DOM output and eventsSource Notes
- Vite documentation, Building for Production
- Vite documentation, Deploying a Static Site
- Vite documentation, Shared Options
- npm documentation,
npm ci - React documentation, Responding to Events