Lorem Ipsum Generator

Generate placeholder text in paragraphs, sentences, or words for your designs and mockups.

Text
100% Free
Runs in Browser

Preview

Source Code

Toggle the controls below the code to live-edit the initial state values.

lorem-generator.tsx
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Copy, Check, RefreshCw } from "lucide-react";

const WORDS = [
    "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit",
    "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore",
    "magna", "aliqua", "enim", "ad", "minim", "veniam", "quis", "nostrud",
    "exercitation", "ullamco", "laboris", "nisi", "aliquip", "ex", "ea", "commodo",
    "consequat", "duis", "aute", "irure", "in", "reprehenderit", "voluptate",
    "velit", "esse", "cillum", "fugiat", "nulla", "pariatur", "excepteur", "sint",
    "occaecat", "cupidatat", "non", "proident", "sunt", "culpa", "qui", "officia",
    "deserunt", "mollit", "anim", "id", "est", "laborum", "perspiciatis", "unde",
    "omnis", "iste", "natus", "error", "voluptatem", "accusantium", "doloremque",
    "laudantium", "totam", "rem", "aperiam", "eaque", "ipsa", "quae", "ab", "illo",
    "inventore", "veritatis", "quasi", "architecto", "beatae", "vitae", "dicta",
];

function generateSentence(minWords = 6, maxWords = 14): string {
    const len = minWords + Math.floor(Math.random() * (maxWords - minWords + 1));
    const words = Array.from({ length: len }, () => WORDS[Math.floor(Math.random() * WORDS.length)]);
    words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);
    return words.join(" ") + ".";
}

function generateParagraph(sentences = 4): string {
    return Array.from({ length: sentences }, () => generateSentence()).join(" ");
}

export default function LoremGenerator() {
    const [count, setCount] = useState(3);
    const [mode, setMode] = useState<"paragraphs" | "sentences" | "words">("paragraphs");
    const [output, setOutput] = useState("");
    const [copied, setCopied] = useState(false);

    const generate = () => {
        if (mode === "paragraphs") {
            setOutput(Array.from({ length: count }, () => generateParagraph()).join("\n\n"));
        } else if (mode === "sentences") {
            setOutput(Array.from({ length: count }, () => generateSentence()).join(" "));
        } else {
            const words = Array.from({ length: count }, () => WORDS[Math.floor(Math.random() * WORDS.length)]);
            words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);
            setOutput(words.join(" ") + ".");
        }
    };

    const copyOutput = async () => {
        await navigator.clipboard.writeText(output);
        setCopied(true);
        setTimeout(() => setCopied(false), 1500);
    };

    return (
        <div className="space-y-4">
            <div className="flex flex-wrap items-center gap-3">
                <span className="text-sm text-muted-foreground">Generate</span>
                <Input
                    type="number"
                    min={1}
                    max={100}
                    value={count}
                    onChange={(e) => setCount(Number(e.target.value) || 1)}
                    className="w-20"
                />
                <div className="flex gap-1">
                    {(["paragraphs", "sentences", "words"] as const).map((m) => (
                        <Button
                            key={m}
                            variant={mode === m ? "default" : "outline"}
                            size="sm"
                            onClick={() => setMode(m)}
                        >
                            {m}
                        </Button>
                    ))}
                </div>
                <Button onClick={generate}>
                    <RefreshCw className="mr-2 h-4 w-4" />
                    Generate
                </Button>
            </div>
            {output && (
                <div className="relative rounded-lg border bg-muted/50 p-4">
                    <p className="whitespace-pre-wrap text-sm leading-relaxed">{output}</p>
                    <Button
                        variant="ghost"
                        size="icon"
                        className="absolute top-2 right-2"
                        onClick={copyOutput}
                    >
                        {copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
                    </Button>
                </div>
            )}
        </div>
    );
}
Props Playground
2 props
3

Required Libraries

Install the external dependencies used by this tool.

bun add lucide-react react

Shadcn UI Setup

Add the required Shadcn UI primitives to your project.

bun x --bun shadcn@latest add badge button input

Shadcn UI Components

The following primitives are required in your @/components/ui directory.

Component Import Path
badge @/components/ui/badge
button @/components/ui/button
input @/components/ui/input

Imports

All import statements used in this tool.

Source Exports
react useState
@/components/ui/button Button
@/components/ui/input Input
@/components/ui/badge Badge
lucide-react Copy, Check, RefreshCw

State Management

React state variables managed within this tool.

Variable Initial Value
count 3
mode "paragraphs"
output ""
copied false

Variables & Constants

Constants and computed values defined in this tool.

Name Value
len minWords + Math.floor(Math.random() * (maxWords - minWord...
words Array.from({ length: len }, () => WORDS[Math.floor(Math.r...
words Array.from({ length: count }, () => WORDS[Math.floor(Math...

Functional Logic

Internal functions that handle the tool's core logic.

Function Parameters Async
generateSentence() minWords = 6, maxWords = 14 No
generateParagraph() sentences = 4 No
generate() None No
copyOutput() None
Yes

External Resources

Documentation, tutorials, and package details for libraries used in this tool.