Every developer has typed "lorem ipsum generator" into Google more times than they'd like to admit. It's one of those small tools you reach for constantly when mocking up a layout, testing how text wraps, or filling a design with placeholder content before the real copy is ready.
Most people just use an existing site for this. But building your own is a genuinely useful exercise, and it's simpler than it looks once you break down what the tool actually needs to do. By the end of this post, you'll have a fully working generator that produces words, sentences, or paragraphs on demand, with a working copy button, built entirely with plain HTML, CSS, and JavaScript. No libraries, no dependencies.
What a Lorem Ipsum Generator Actually Needs to Do
Strip away the placeholder text tradition and what you're really building is a controlled random text generator. It needs to:
- Pull words from a fixed vocabulary (the classic lorem ipsum word list)
- Assemble those words into sentences with proper capitalization and punctuation
- Group sentences into paragraphs
- Let the user choose how much text they want, and in what unit (words, sentences, or paragraphs)
That's the entire logic. The hard part isn't randomness, it's making the randomness look like real text instead of an obviously shuffled word salad. Real sentences vary in length, start with a capital letter, end with punctuation, and occasionally contain a comma. A generator that ignores these details produces output that looks mechanical the moment you glance at it.
Step 1: The Word Bank
Start with the standard lorem ipsum vocabulary. You don't need the entire original Cicero passage, just enough words to keep the output from looking repetitive.
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"
];
About 60 words is plenty. Any more and you're just adding noise without meaningfully changing the output's feel.
Step 2: Generating a Single Sentence
This is where the real logic lives. A sentence needs a random length, a capitalized first letter, a period at the end, and occasionally a comma somewhere in the middle to break up the rhythm.
function getRandomWord() {
return words[Math.floor(Math.random() * words.length)];
}
function generateSentence() {
const length = Math.floor(Math.random() * 10) + 5; // 5 to 14 words
let sentenceWords = [];
for (let i = 0; i < length; i++) {
sentenceWords.push(getRandomWord());
}
// Occasionally insert a comma partway through, but not too early or late
if (length > 6 && Math.random() > 0.6) {
const commaPosition = Math.floor(length / 2);
sentenceWords[commaPosition] += ",";
}
let sentence = sentenceWords.join(" ");
sentence = sentence.charAt(0).toUpperCase() + sentence.slice(1) + ".";
return sentence;
}
Notice the length is randomized within a range, not fixed. Real sentences aren't all the same size, and fixed-length output is one of the easiest ways to make generated text look fake at a glance. The comma logic is optional polish, but it's the kind of small detail that makes the output read more naturally when someone's actually proofreading a layout.
Step 3: Building Paragraphs from Sentences
A paragraph is just a handful of sentences joined together, again with a randomized count so paragraphs don't all feel uniform.
function generateParagraph() {
const sentenceCount = Math.floor(Math.random() * 4) + 3; // 3 to 6 sentences
let sentences = [];
for (let i = 0; i < sentenceCount; i++) {
sentences.push(generateSentence());
}
return sentences.join(" ");
}
Step 4: Tying It to User Input
Now the part that makes this an actual tool instead of just a script. The user needs to choose a unit (words, sentences, or paragraphs) and a quantity, then get output matching that choice.
function generateText(unit, count) {
if (unit === "words") {
let result = [];
for (let i = 0; i < count; i++) {
result.push(getRandomWord());
}
let text = result.join(" ");
return text.charAt(0).toUpperCase() + text.slice(1) + ".";
}
if (unit === "sentences") {
let result = [];
for (let i = 0; i < count; i++) {
result.push(generateSentence());
}
return result.join(" ");
}
if (unit === "paragraphs") {
let result = [];
for (let i = 0; i < count; i++) {
result.push(generateParagraph());
}
return result.join("\n\n");
}
}
Three branches, one for each unit type, each building on the functions from the previous steps. This is the core engine. Everything else (the form, the buttons, the copy function) is just the interface wrapped around this.
Step 5: Wiring Up the HTML Form
<div class="generator">
<label for="unit">Generate:</label>
<select id="unit">
<option value="words">Words</option>
<option value="sentences">Sentences</option>
<option value="paragraphs" selected>Paragraphs</option>
</select>
<label for="count">Amount:</label>
<input type="number" id="count" value="3" min="1" max="50">
<button id="generateBtn">Generate</button>
<button id="copyBtn">Copy</button>
</div>
<textarea id="output" rows="12" readonly></textarea>
document.getElementById('generateBtn').addEventListener('click', () => {
const unit = document.getElementById('unit').value;
const count = parseInt(document.getElementById('count').value, 10);
document.getElementById('output').value = generateText(unit, count);
});
At this point the generator is fully functional. Pick a unit, set a number, click generate, and the textarea fills with text matching the choice.
Step 6: Adding a Working Copy Button
A placeholder text tool without a one click copy button isn't really finished. People using this are usually about to paste the result straight into a design mockup or a CMS field, so make that step effortless.
document.getElementById('copyBtn').addEventListener('click', () => {
const output = document.getElementById('output');
navigator.clipboard.writeText(output.value).then(() => {
const btn = document.getElementById('copyBtn');
const original = btn.textContent;
btn.textContent = "Copied!";
setTimeout(() => { btn.textContent = original; }, 1500);
});
});
The navigator.clipboard API handles the actual copying. The brief "Copied!" label swap is a small confirmation that tells the user the click registered, which matters more than it seems. Without that feedback, people often click the button two or three times out of uncertainty.
A Detail Most Tutorials Get Wrong: Starting with "Lorem Ipsum"
If you've used any lorem ipsum generator before, you've probably noticed the output almost always starts with the exact phrase "Lorem ipsum dolor sit amet" before moving into random text. This isn't an accident or a leftover bug, it's expected behavior, since that opening phrase is what makes the output immediately recognizable as placeholder text rather than something a careless person might mistake for a typo-filled real sentence.
You can bake this in by hardcoding the first sentence when generating paragraphs or longer word counts:
function generateText(unit, count) {
const opening = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
if (unit === "paragraphs") {
let result = [generateParagraph()];
result[0] = opening + " " + result[0];
for (let i = 1; i < count; i++) {
result.push(generateParagraph());
}
return result.join("\n\n");
}
// word and sentence branches stay the same, or apply similar logic
}
This is a small thing, but it's the difference between a generator that feels authentic to the tradition and one that just produces generic shuffled Latin words.
Styling It So It Doesn't Look Like a School Project
.generator {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
margin-bottom: 16px;
}
.generator select,
.generator input,
.generator button {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 14px;
}
.generator button {
background-color: #2563eb;
color: white;
border: none;
cursor: pointer;
}
.generator button:hover {
background-color: #1d4ed8;
}
#output {
width: 100%;
padding: 16px;
font-family: monospace;
font-size: 14px;
border-radius: 8px;
border: 1px solid #ccc;
resize: vertical;
}
Nothing fancy here, just enough spacing and color so the controls look intentional instead of default browser styling stacked in a column.
Why Build This Instead of Just Using an Existing Site
There's a practical reason beyond the learning exercise. If you build internal tools, admin dashboards, or CMS interfaces for clients, having your own lightweight generator means you can drop it directly into a project without linking out to a third party tool or worrying about an external site going down, changing its layout, or adding ads around your embed.
It's also a genuinely good small project for practicing core JavaScript skills, working with arrays, randomization, string manipulation, and DOM events, without the complexity of a larger app getting in the way of the fundamentals.
The Bottom Line
A real lorem ipsum generator comes down to four pieces: a word list, a sentence builder with randomized length and punctuation, a paragraph builder stacking those sentences, and a thin interface layer connecting user input to the output. None of it requires a framework or a library, and the entire thing fits comfortably in under 100 lines of JavaScript.
Once you've built one version, it's easy to extend, custom word lists for different niches, an HTML output mode with wrapped paragraph tags, or even a character count limit instead of word or sentence counts. The core engine stays the same either way.