arnobt78/Paragraph-Generator--React-Fundamental-Project-8
A modern, interactive React application that generates a customizable number of random paragraphs ("Lorem Ipsum") for use in design, development, and content prototyping. This project demonstrates fundamental React concepts, state management, and functional component design with practical, real-world utility
Paragraph Generator – React, Vite, JavaScript, Custom CSS Fundamental Project 8
A modern, interactive React application that generates a customizable number of random paragraphs ("Lorem Ipsum") for use in design, development, and content prototyping. This project demonstrates fundamental React concepts, state management, and functional component design with practical, real-world utility.
- Live Demo: https://create-paragraph.vercel.app/
Table of Contents
- Project Summary
- Features
- Technology Stack
- Project Structure
- Component Overview
- How It Works (Walkthrough)
- Setup & Usage
- Environment Variables (.env)
- Key Concepts & Teaching Content
- Reusing Components in Other Projects
- API, Backend & Routes
- Customization
- Keywords
- Conclusion
- License
Project Summary
The Paragraph Generator is a learning-focused React project that lets users generate a desired number of random placeholder paragraphs (1–8) with a single form submit. It showcases form handling, state management (useState), and dynamic list rendering in React, making it a solid resource for beginners and anyone looking for a clear example of React fundamentals.
The UI is minimal and accessible: users enter a number between 1 and 8, click Generate, and the app instantly displays that many paragraphs below the form. There is no backend or API—all data lives in the frontend, so the project is ideal for studying React in isolation.
Features
- Custom paragraph count: Choose how many paragraphs to show (1–8).
- Form handling: Number input with
min/maxvalidation and controlled component pattern. - Live preview: Generated paragraphs appear immediately on submit.
- Stable list keys: Uses
nanoidfor unique keys in.map()to satisfy React’s reconciliation and avoid key warnings. - Accessible UI: Semantic HTML (
<section>,<form>,<label>,<article>) and associated form controls. - Fast tooling: Vite for quick dev server and optimized production builds.
- Lint: ESLint with React and React Hooks rules; run with
npm run lint.
Technology Stack
| Technology | Purpose |
|---|---|
| React 18 | UI library; functional components and hooks |
| Vite 4 | Dev server, HMR, and production bundling |
| JavaScript (ES6+) | No TypeScript; modern syntax and modules |
| nanoid | Small library for unique IDs (list keys) |
| CSS | Custom global and component styles; CSS variables |
| HTML5 | Single index.html as app shell |
Project Structure
08-lorem-ipsum/
├── public/
│ └── vite.svg # Favicon / static asset
├── src/
│ ├── App.jsx # Main component: form + paragraph list
│ ├── data.js # Array of 8 paragraph strings (data source)
│ ├── index.css # Global + project-specific CSS
│ └── main.jsx # React entry: mounts app into #root
├── index.html # HTML template and SEO meta tags
├── vite.config.js # Vite config (React plugin)
├── eslint.config.js # ESLint flat config (React + Hooks)
├── package.json
└── README.md- Entry:
index.htmlloadssrc/main.jsx, which renders<App />into#root. - Data:
src/data.jsexports one array; no backend or API. - Styling:
src/index.cssis imported inmain.jsxand applies globally.
Component Overview
main.jsx (Entry)
- Imports React, ReactDOM,
App, andindex.css. - Uses
createRootandrenderto mount<App />inside<div id="root">. - Wraps the app in
<React.StrictMode>for development checks.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);App.jsx (Main UI)
- State:
count(number of paragraphs to show, default1),text(array of paragraph strings to render). - Form: Number input (min 1, max 8), label, and submit button. On submit,
handleSubmitreads the value, parses it, slicesdatato that length, and updatestext. - Output: Renders an
<article>that maps overtextand shows each item in a<p>with ananoid()key.
Relevant snippets:
const [count, setCount] = useState(1);
const [text, setText] = useState([]);
const handleSubmit = (e) => {
e.preventDefault();
let amount = parseInt(count);
setText(data.slice(0, amount));
};<article className="lorem-text">
{text.map((item) => {
return <p key={nanoid()}>{item}</p>;
})}
</article>data.js (Data Module)
- Exports a single array of 8 strings. Each string is one paragraph (various placeholder themes: candy, zombie, cat, cheese, etc.).
- Used only by
App.jsxviadata.slice(0, amount)to select how many paragraphs to display.
index.css (Styles)
- Global: Reset (margin/padding/box-sizing),
:rootvariables (colors, spacing, shadows, typography), base styles forbody, headings, buttons, forms, alerts. - Project-specific:
.section-center,.lorem-form,.lorem-textfor layout and appearance of the generator UI. - Reusable utility classes (e.g.
.btn,.alert,.form) can be reused in other pages or components.
How It Works (Walkthrough)
- Page load:
main.jsxmountsApp. Initial state:count = 1,text = [], so no paragraphs are shown yet. - User input: User changes the number input (1–8). Because the input is controlled (
value={count},onChangeupdatingcount), React re-renders with the new value. - Submit: User clicks Generate.
handleSubmitruns:e.preventDefault()stops full page submit;amount = parseInt(count);setText(data.slice(0, amount))sets state to the firstamountitems fromdata. - Re-render: React re-renders
App. The.map()overtextproduces one<p>per paragraph, each with a uniquekey={nanoid()}. - Result: The requested number of paragraphs appears below the form. Changing the number and clicking Generate again updates the list.
No server, no API, no routing—everything happens in the browser with React state and the static data array.
Setup & Usage
Prerequisites
- Node.js (v14 or higher recommended)
- npm (or yarn/pnpm)
Installation
-
Clone the repository
git clone https://github.com/arnobt78/Paragraph-Generator--React-Fundamental-Project-8.git cd Paragraph-Generator--React-Fundamental-Project-8 -
Install dependencies
npm install
Run locally
npm run devThen open the URL shown in the terminal (typically http://localhost:5173). Use the form to pick a number (1–8) and click Generate to see the paragraphs.
Build for production
npm run buildOutput goes to the dist/ folder. To preview the production build locally:
npm run previewLint
npm run lintRuns ESLint on src (and any other configured paths). Fix any reported errors or warnings to keep the codebase consistent.
Environment Variables (.env)
This project does not use any environment variables. All behavior is driven by the code and the static data.js file. There is no API key, no backend URL, and no feature flags.
- The repo’s
.gitignorealready includes.envso that if you add a.envfile later (e.g. for an optional API or analytics), it won’t be committed. - If you extend the project (e.g. add an API or analytics):
- Create a
.envfile in the project root. - In Vite, use
import.meta.env.VITE_*for variables you want in the client (e.g.VITE_API_URL). - Add
.env.examplewith placeholder names (no secrets) and document each variable in the README.
- Create a
For this educational version, no .env file or environment variables are required to run or build the app.
Key Concepts & Teaching Content
1. useState Hook
State is stored and updated with useState. When the setter is called, React re-renders the component and any children that depend on that state.
const [count, setCount] = useState(1); // number of paragraphs
const [text, setText] = useState([]); // array of strings to show2. Controlled Input
The number input is “controlled”: its value is count and changes go through setCount, so React always owns the value.
<input
type="number"
value={count}
onChange={(e) => setCount(e.target.value)}
min="1"
max="8"
/>3. Form Submit Handler
Prevent default form submission and derive the amount from state (or from the event target if you prefer), then update state.
const handleSubmit = (e) => {
e.preventDefault();
let amount = parseInt(count);
setText(data.slice(0, amount));
};4. Slicing an Array
Array.prototype.slice(0, n) returns the first n elements without mutating the original array—ideal for “first N paragraphs.”
setText(data.slice(0, amount));5. List Rendering and Keys
When rendering lists, each item must have a stable, unique key so React can reconcile efficiently. Here, nanoid() generates a new ID per item.
{
text.map((item) => <p key={nanoid()}>{item}</p>);
}6. Single Source of Truth
Paragraph content lives only in data.js. The UI only decides how many to show; it doesn’t duplicate or modify the original data.
Reusing Components in Other Projects
Reusing the “paragraph generator” behavior
- Copy
App.jsx(and optionally rename it, e.g.ParagraphGenerator.jsx). Keep the same logic:useStatefor count and text, form with number input,handleSubmitthat doesdata.slice(0, amount), and the.map()overtext. - Copy or adapt
data.js—either the same array or your own list of strings. - Install
nanoidin the other project:npm install nanoid. - Styling: Copy the relevant parts of
index.css(e.g..section-center,.lorem-form,.lorem-text, and any variables/buttons you use), or replace with your own CSS/styled-components.
Reusing only the form pattern
- Use the same controlled input +
handleSubmitpattern with your own state and logic (e.g. different limits, different data source, or fetching from an API instead ofdata.js).
Reusing only the data
- Import
datafromdata.js(or a copy) in any component that needs placeholder text—e.g. for a preview, a modal, or a different layout.
API, Backend & Routes
- API: None. The app does not call any external or internal API. All text comes from the in-memory
dataarray. - Backend: None. The project is frontend-only. Deployment is typically static (e.g. Vercel, Netlify) serving the built
dist/output. - Routes: Single page. There is no router (no React Router or similar). One URL, one screen: the paragraph generator form and result.
Customization
- Paragraph content: Edit
src/data.jsto add, remove, or change the strings. Adjust themaxon the input inApp.jsxif you add more than 8. - Paragraph limits: In
App.jsx, change the input’sminandmax(and optionally the initialuseState(1)default) to allow a different range (e.g. 1–20). - Styling: Change
src/index.css—especially:rootvariables and the.section-center,.lorem-form,.lorem-textrules—to match your theme or layout. - Copy and headings: Change the
<h4>and button/label text inApp.jsxto fit your branding or language.
Keywords
React, Paragraph Generator, Lorem Ipsum, Vite, useState, nanoid, JavaScript, Functional Components, Form Handling, Array.slice, Random Text, Placeholder Text, Frontend, CSS, Web App, Beginner Project, Controlled Input, List Rendering, React Hooks, ESLint
Conclusion
This Paragraph Generator project is a small, self-contained example of React fundamentals: state, controlled inputs, form handling, and list rendering with stable keys. It has no backend or environment variables, so you can run and modify it immediately. Use it to learn React, to share with others, or as a starting point for a more advanced app (e.g. with an API, routing, or more complex UI).
License
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
Happy Coding! 🎉
This is an open-source project — feel free to use, enhance, and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://www.arnobmahmud.com.
Enjoy building and learning! 🚀
Thank you! 😊
