GitHunt
AR

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.

License: MIT
React
Vite
JavaScript

Screenshot 2026-03-11 at 15 03 23

Table of Contents


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/max validation and controlled component pattern.
  • Live preview: Generated paragraphs appear immediately on submit.
  • Stable list keys: Uses nanoid for 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.html loads src/main.jsx, which renders <App /> into #root.
  • Data: src/data.js exports one array; no backend or API.
  • Styling: src/index.css is imported in main.jsx and applies globally.

Component Overview

main.jsx (Entry)

  • Imports React, ReactDOM, App, and index.css.
  • Uses createRoot and render to 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, default 1), text (array of paragraph strings to render).
  • Form: Number input (min 1, max 8), label, and submit button. On submit, handleSubmit reads the value, parses it, slices data to that length, and updates text.
  • Output: Renders an <article> that maps over text and shows each item in a <p> with a nanoid() 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.jsx via data.slice(0, amount) to select how many paragraphs to display.

index.css (Styles)

  • Global: Reset (margin/padding/box-sizing), :root variables (colors, spacing, shadows, typography), base styles for body, headings, buttons, forms, alerts.
  • Project-specific: .section-center, .lorem-form, .lorem-text for 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)

  1. Page load: main.jsx mounts App. Initial state: count = 1, text = [], so no paragraphs are shown yet.
  2. User input: User changes the number input (1–8). Because the input is controlled (value={count}, onChange updating count), React re-renders with the new value.
  3. Submit: User clicks Generate. handleSubmit runs: e.preventDefault() stops full page submit; amount = parseInt(count); setText(data.slice(0, amount)) sets state to the first amount items from data.
  4. Re-render: React re-renders App. The .map() over text produces one <p> per paragraph, each with a unique key={nanoid()}.
  5. 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

  1. Clone the repository

    git clone https://github.com/arnobt78/Paragraph-Generator--React-Fundamental-Project-8.git
    cd Paragraph-Generator--React-Fundamental-Project-8
  2. Install dependencies

    npm install

Run locally

npm run dev

Then 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 build

Output goes to the dist/ folder. To preview the production build locally:

npm run preview

Lint

npm run lint

Runs 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 .gitignore already includes .env so that if you add a .env file 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 .env file 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.example with placeholder names (no secrets) and document each variable in the README.

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 show

2. 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

  1. Copy App.jsx (and optionally rename it, e.g. ParagraphGenerator.jsx). Keep the same logic: useState for count and text, form with number input, handleSubmit that does data.slice(0, amount), and the .map() over text.
  2. Copy or adapt data.js—either the same array or your own list of strings.
  3. Install nanoid in the other project: npm install nanoid.
  4. 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 + handleSubmit pattern with your own state and logic (e.g. different limits, different data source, or fetching from an API instead of data.js).

Reusing only the data

  • Import data from data.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 data array.
  • 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.js to add, remove, or change the strings. Adjust the max on the input in App.jsx if you add more than 8.
  • Paragraph limits: In App.jsx, change the input’s min and max (and optionally the initial useState(1) default) to allow a different range (e.g. 1–20).
  • Styling: Change src/index.css—especially :root variables and the .section-center, .lorem-form, .lorem-text rules—to match your theme or layout.
  • Copy and headings: Change the <h4> and button/label text in App.jsx to 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! 😊

arnobt78/Paragraph-Generator--React-Fundamental-Project-8 | GitHunt