padEnd()
ES2017+Pads the end of a string with another string, repeated and truncated as needed, until the result reaches a target length, then returns that new string. Input that already meets the target length is returned unchanged, never truncated. The default pad is a space; added in ES2017 alongside padStart().
Syntax
string.padEnd(targetLength, padString)Parameters
targetLength number The length of the resulting string
padString string optionalThe string to pad with (default: space)
Return Value
The padded string
Examples
const str = 'abc';
console.log(str.padEnd(6, '123')); 📌 When to Use
Use padEnd() to left-align text within fixed-width slots: label columns in console tables and CLI output, aligned key-value listings in logs, fixed-width field records for legacy interchange formats, dot or dash leaders between a label and a value, and monospace layouts in emails or plain-text reports. The division of labor with padStart() follows typography: text reads left-aligned, so labels and names take padEnd(), while numbers compare by their rightmost digits, so numeric columns take padStart(); a well-formatted table typically uses both. As with its sibling, targetLength is the total resulting length, the method never truncates over-long input, and the pad string may be multi-character, truncated to fit, which makes '.'.repeat-style leader lines a one-liner via padEnd(width, '.'). Compute column widths from the data, taking the maximum length per column including the header, and the layout adapts to its content. Alignment only materializes in monospace contexts, terminals, code blocks, and pre-formatted text, since proportional fonts ignore character-count alignment entirely; in HTML use CSS or table layout instead. Lengths count UTF-16 code units, so emoji and wide CJK glyphs will bend visual alignment even when the counts are right. For trailing-zero decimal alignment, prefer toFixed(), which is numeric formatting, not string padding.
⚠️ Common Mistakes
Treating targetLength as the amount of padding: 'abc'.padEnd(6) produces a six-character result with three spaces added, not six. Widths must be the intended total, usually the maximum content width of the column.
Forgetting that over-long content is not truncated: one long cell pushes everything after it out of alignment because padEnd() passes it through unchanged. Fixed layouts need an explicit slice() after padding, or widths computed from the actual maximum content length.
Expecting alignment in proportional fonts: space-padded columns only line up where every character has equal width, so the technique belongs to terminals, code blocks, and pre elements; in a normal web page the padding is invisible and CSS should do the layout.
Calling it on numbers directly: numeric values must be converted with String() first or the call throws; and unlike padStart() for numbers, padding numbers on the right rarely makes sense except for decimal-point alignment, where toFixed() is the correct tool.
Counting code units and trusting visual width: emoji occupy two code units and render wide, CJK characters one unit but double width in most terminals, so tables containing them misalign despite correct length math; truly robust terminal layout needs a display-width library.
Building trailing whitespace into stored data: padEnd() output is for display; persisting padded values means every consumer must trim them back, and equality comparisons quietly fail. Pad at the output edge only.
✅ Best Practices
Compute column widths from the data: take the maximum of the header length and every cell's length per column, then padEnd() each cell to that width, so the table fits its content instead of guessing.
Follow the alignment convention: padEnd() for text columns, padStart() for numeric columns, which makes tables read naturally and keeps digits comparable at a glance.
Use a multi-character pad for leader lines, as in label.padEnd(40, '.') before a value, a tidy idiom for settings listings and tables of contents.
Combine padEnd() with slice() when a hard fixed width is contractual, as in legacy record formats, so both short and long values land at exactly the field width.
Keep padding at the presentation layer: store clean values, pad on output, and trimEnd() anything padded that must be read back.
For CJK- or emoji-bearing tables in terminals, measure display width with a dedicated library instead of relying on length, or normalize the data before layout.
⚡ Performance Notes
padEnd() allocates one output string of the target length and copies the source plus the repeated pad into it, so cost scales with output size and is trivial for the column widths where the method is used. Input already at or beyond the target is returned as-is with no allocation, making blanket padding of mostly-full columns essentially free. It is faster and clearer than assembling the same result from repeat() and concatenation, which allocates intermediates and duplicates the clamping logic. In table rendering, the practical costs sit elsewhere: computing per-column maxima is a full pass over the data, and the final assembly should collect rows into an array and join once rather than growing one giant string incrementally. At thousands of rows, all of this remains far cheaper than the console or file output it feeds, so favor the most readable formulation.
🌍 Real World Example
Console Tables and Text Alignment
Console table rendering, the method's home ground. printTable() first measures each column, taking the larger of the header length and the longest cell, then pads every header and cell to its column width with padEnd(), joining cells with separators so rows align perfectly in monospace output; this is exactly how CLI tools print result tables without a table library. formatFixedWidth() applies the same idea to legacy fixed-width records, padding each field to its contractual width so positions, not delimiters, define the fields. showProgress() pads task names so multiple progress bars start at the same column, a small touch that makes concurrent task output readable.
// Create a formatted table output
function printTable(headers, rows) {
// Calculate column widths
const widths = headers.map((h, i) =>
Math.max(h.length, ...rows.map(row => String(row[i]).length))
);
// Print header
const headerLine = headers
.map((h, i) => h.padEnd(widths[i]))
.join(' | ');
console.log(headerLine);
console.log('-'.repeat(headerLine.length));
// Print rows
rows.forEach(row => {
console.log(
row.map((cell, i) => String(cell).padEnd(widths[i])).join(' | ')
);
});
}
printTable(
['Name', 'Role', 'Salary'],
[
['Alice', 'Developer', 75000],
['Bob', 'Designer', 65000],
['Charlie', 'Manager', 90000]
]
);
// Name | Role | Salary
// --------------------------------
// Alice | Developer | 75000
// Bob | Designer | 65000
// Charlie | Manager | 90000
// Fixed-width field formatting for files
function formatFixedWidth(record, fieldWidths) {
return Object.entries(record)
.map(([key, value], i) => String(value).padEnd(fieldWidths[i]))
.join('');
}
const record = { id: '001', name: 'Smith', amount: '150.00' };
console.log(formatFixedWidth(record, [5, 20, 10]));
// '001 Smith 150.00 '
// Progress display with description
function showProgress(task, percent) {
const bar = '█'.repeat(percent / 5) + '░'.repeat(20 - percent / 5);
console.log(`${task.padEnd(20)} [${bar}] ${percent}%`);
}
showProgress('Downloading', 75);
// 'Downloading [███████████████░░░░░] 75%'