Skip to content

CSV mail merge

Turn a template and CSV rows into one personalized message per row.

Input
CSV data
Output

CSV mail merge

Write a message template once, drop your CSV rows into the second panel, and this tool produces one personalized message per row. Mark the parts that change with a placeholder — {{first_name}}, {{company}}, {{amount_due}} — and every row's own values are substituted in on generation. It supports three placeholder styles: {{column}}, {column} and $column, so you can match whatever your source template already uses.

The CSV side reads your header row as column names by default, so placeholders line up with the sheet. No header row is fine too — columns are then referenced positionally as Column 1, Column 2 and so on. Delimiters are auto-detected, or force comma, semicolon, tab or pipe. An empty cell can take a fallback value, or the whole row can be skipped when "Skip rows with a missing value" is on — handy when a message can't go out without that field. A case option can capitalize, uppercase or lowercase each substituted value without touching the surrounding text, and cell whitespace is trimmed by default.

Between messages you choose the separator — a blank line for a scrollable list, or a single newline for piping the result elsewhere. Numbering prefixes each message with its position, and the unused-columns warning catches CSV columns the template never references and placeholders with no matching column — a quick way to spot a typo before sending a hundred messages with the wrong name in them.

Everything runs locally in your browser: your template and your CSV data — customer names, email addresses, order details — are never uploaded anywhere. Copy the finished messages, download them as a .txt file, or send the output into another tool to keep processing it.

FAQ

Which placeholder styles are supported?
Three: {{column}} (double braces), {column} (single braces) and $column (a dollar sign followed by letters, digits or underscores). Pick the one that matches your template in the "Placeholder syntax" option.
What happens if a placeholder has no matching column?
It's left in the message exactly as written and listed under "Unfilled placeholders" when the warning option is on — nothing is silently dropped.
Can I skip rows instead of filling in a fallback value?
Yes. Turn on "Skip rows with a missing value" and any row where a used column is empty is left out of the generated messages entirely, instead of using the fallback text.
Does it need a header row?
No. Turn off "First row is header" and every row, including the first, is treated as data — columns are then referenced as {{Column 1}}, {{Column 2}} and so on.
Is my CSV data uploaded anywhere?
No. The template and the CSV are merged entirely in your browser, and neither one ever leaves your device.