Hashtag Formatter/Cleaner
Paste messy phrases or half-formed tags and turn each one into a clean, valid hashtag with no spaces or stray punctuation.
About the Hashtag Formatter/Cleaner
This is neither the Hashtag Generator (which invents new tags from a topic) nor the Hashtag Extractor (which pulls existing tags out of a caption). This one takes messy raw entries, comma or newline separated, some already starting with #, some with spaces, punctuation, or hyphens, and repairs each into a hashtag a platform will actually accept: any leading # is stripped, every character that isn't a letter, number, or underscore is removed (real hashtags can't contain spaces or most symbols), and the result is rejoined into either lowercase (spaces removed, so "social media tips" becomes "socialmediatips") or PascalCase (each word capitalized and concatenated, so "social media tips" becomes "SocialMediaTips", useful for readability since a hashtag can't otherwise contain spaces). A single # is re-added at the front, and duplicates are dropped from the final list.
Why splitting on non-word characters works better than simply deleting them outright
The cleaning logic doesn't simply strip out spaces, hyphens, and punctuation from an entry, it splits the entry apart wherever any of those characters occur, treating each stretch of non-word characters as a word boundary rather than something to just delete in place. That distinction matters for PascalCase specifically: "web-dev" splits into the two separate words ["web", "dev"], which then each get their own capital letter, "WebDev". If the hyphen were simply deleted before splitting, there'd be no way to know where one word ended and the next began, and the tool couldn't correctly capitalize the second word at all.
PascalCase only forces the first letter of each word, nothing else
Look closely at the capitalization logic and you'll notice it only touches a word's very first character, word.charAt(0).toUpperCase() + word.slice(1), everything after that first letter passes through completely unchanged. That means a word that's already mixed-case internally, like "iPhone," becomes "IPhone" in PascalCase mode, only the leading "i" gets forced to uppercase, the rest of the word's original casing survives untouched. Lowercase mode behaves completely differently here: it runs .toLowerCase() across the entire joined result, forcing every character down regardless of position, so the same "iPhone" entry becomes a fully lowercase "iphone" in that mode instead. The two casing modes aren't just opposite directions of the same transformation, they apply their case changes at genuinely different scopes.
Why the cleaning logic runs a final pass after the casing step, not just once upfront
Characters get filtered out twice: once implicitly, when the entry is split into words using only letter/digit/underscore runs, and again explicitly, right after the words are joined into their final casing. That second pass exists as a safety net specifically because the casing and joining step happens in between the two cleanups, guaranteeing that whatever comes out the other end is verified clean regardless of what intermediate transformations occurred, rather than trusting a single upfront pass to have caught everything correctly before the rest of the pipeline runs. If an entry cleans down to nothing at all, empty, whitespace, or pure punctuation, it's simply dropped from the results rather than producing a bare, meaningless "#" on its own line.
Why the input accepts both commas and newlines as separators
The entry list is split on any run of commas or newline characters, /[,\n]+/, which means you can paste either a single inline comma-separated line or a list copied from a spreadsheet column (one entry per line) without needing to reformat it into a specific shape first. Mixing both styles in the same paste, some entries comma-separated, others on their own line, also works fine, since the splitting pattern treats either separator, or a run of either, identically, and empty entries left behind by trailing separators are silently discarded rather than turning into blank output rows.
Frequently Asked Questions
Why does "web-dev" become "WebDev" instead of "Webdev" in PascalCase mode?
The cleaner splits entries into words wherever a non-letter, non-digit character appears, rather than just deleting those characters. "web-dev" splits into two separate words, "web" and "dev", so each gets its own capital letter, producing "WebDev" instead of accidentally merging them into one incorrectly-cased word.
Why does "iPhone" become "IPhone" rather than "Iphone" in PascalCase mode?
PascalCase only forces the very first character of each word to uppercase, everything after that passes through unchanged. Since "iPhone" already has its own internal capitalization, only the leading "i" gets forced up, producing "IPhone" rather than flattening the rest of the word's casing.
Does lowercase mode work the same way as PascalCase, just inverted?
No, lowercase mode forces every character in the entire joined result to lowercase, not just the first letter of each word. PascalCase and lowercase apply their case changes at genuinely different scopes, one touches only leading letters, the other touches everything uniformly.
Why does the cleaning logic filter out invalid characters twice?
Once implicitly when splitting entries into words, and again explicitly after the words are joined into their final casing. The second pass acts as a safety net, guaranteeing the final output is verified clean regardless of what happened during the casing and joining step in between.
Can I paste a list separated by commas and one separated by line breaks at the same time?
Yes, the input is split on any run of commas or newlines, so mixing both styles in the same paste works identically to using just one consistently, no reformatting needed beforehand.
What happens if I enter the same messy phrase twice with different formatting?
Duplicates are detected case-insensitively after cleaning, so "Social Media" and "social-media" would both clean into the same tag and only appear once in the final list, keeping whichever version was encountered first.