The guide
A hashtag ends where a rule says it ends, and every service keeps its own rule
Nobody owns the definition of a hashtag, so one caption can hold five tags in one place and fewer in another. Clear capitals and small reusable sets survive that. Popularity numbers do not.
- U+0023 the one character that starts a tag under this page's rule
- 2007 the year the hashtag was first proposed
- 5 tags read from the six number signs of the worked caption
- 16 characters SmallBatchCoffee, counted behind its number sign
- 22 words the whole built-in list for unlucky joins, stated as incomplete
Read the full guide Fold the guide
A hashtag looks like the simplest piece of syntax on the internet: a number sign, then a word. The trouble is at the far end. Something has to decide where the word stops, and that something is a piece of software written by whoever runs the place you are posting to. No single definition is shared by all of them. That matters every time a caption holds a hyphen, an apostrophe, an accent or an emoji, which is to say most of the time.
Where a tag starts and where it stops
The convention is young and it was never standardised at birth. The use of hashtags was first proposed in 2007, in a short post on a microblogging service, by Chris Messina, who took the number sign from the channel names of an older chat system and made no attempt to patent the idea. It spread by imitation. Each service that adopted it wrote its own reader for it.
The Unicode Consortium says as much. Its annex on identifiers notes that the composition of allowable hashtag identifiers varies between vendors, and then offers a default syntax to improve matters. In that default a tag may start at the number sign or at its small and full-width forms. It continues through identifier characters, pictographs and emoji components, the underscore, the hyphen-minus and the plus sign. The annex also recommends reading a tag only when no such character stands directly before the sign, so that a sign in the middle of a word starts nothing.
Two open-source parsers show how far practice sits from that default. The test file of one widely copied library refuses a tag made only of digits, refuses a tag with no letter at all, keeps a middle dot inside a Catalan word, and keeps the invisible joiners that Arabic-script and Sinhala words need. The source of one federated server accepts the full-width sign, demands a letter, and lets the underscore, two kinds of middle dot and the zero width non-joiner sit inside a tag. A hyphen is on neither of that server’s lists. Both were read as published code and as evidence that rules differ, not as a description of what any live service does today.
So the honest thing a tool can do is choose one rule, state it once, and show its work. The workshop on this page uses a narrow rule on purpose. A tag starts at U+0023 and at nothing else. It runs through letters, combining marks, decimal digits and the underscore. It ends at the first character of any other kind. Digits alone are not a tag.
Those classes are not a regular-expression shorthand, whose meaning shifts between engines. They are the General_Category values of the Unicode Character Database, which gives every code point a category such as letter, nonspacing mark, decimal number or connector punctuation. The underscore is a connector punctuation character, and it is the only one of that class the rule admits.
A worked caption
Here is a caption of the kind people write. New roast day at the shop! #SmallBatchCoffee #rock-n-roll breakfast, #don't miss it. #2026 #café, and last #latte followed at once by a yellow heart emoji and the letters art. It has six number signs. A hurried writer would say it has six tags.
Under the rule above it has 5 tags, and three of them are not the tags the writer meant. The second stops at the hyphen-minus, U+002D, so the tag is only rock and the rest trails behind as plain text. The third stops at the apostrophe and reads as don. The last stops at the heart, so the tag is latte. The fourth sign is followed by digits alone and is no tag at all. Only the first and the accented one arrive whole.
Read the same caption with the Unicode default and the answer changes. The hyphenated tag survives in one piece, and the heart becomes part of the last tag. Read it with a parser that needs a letter and the year fails there too, for a different stated reason. One caption, three sets of tags. No page can settle which is right, because each is right somewhere, and the only test that counts is the place where you will post. A checker can still earn its keep there, by showing you which characters are worth testing before you publish.
The repair is to stop typing tags and start typing words. Give the builder the words rock n roll and it joins them into RockNRoll, with nothing for a parser to trip on. Give it small batch coffee and the result is SmallBatchCoffee, which is 16 characters behind its number sign. The hyphen and the apostrophe are taken out, named, and shown, so nothing vanishes silently.
What a reader sees and what a parser sees
A reader counts characters by eye. Unicode calls those user-perceived characters grapheme clusters, and one cluster may be several code points. The accented letter in the example can be stored as a single code point or as a plain e followed by a combining acute accent. Normalization Form C composes the pair into one. A rule that counts combining marks as tag characters keeps both spellings whole, and a browser can segment text into clusters for display.
Invisible characters are harder. An emoji of a person at a laptop is several emoji held together by a zero width joiner, and the same joiner sits inside ordinary words in some scripts. Under a narrow rule that joiner ends a tag at a spot where the eye sees nothing. A careful tool therefore shows the code point, because there is no glyph to show. It should also say that other parsers keep the joiner, since for a Sinhala or Persian writer that difference decides whether the tag works.
Sameness needs a rule as well. The Unicode annex recommends comparing tags after normalising and case folding them, so that the same word in different capitals matches. The workshop folds with Form C and the browser’s case mappings. That is narrower than the annex suggests, because it leaves full-width letters and accents distinct, and the page says so. The useful consequence is that capitals are free. Changing them never changes which tag it is.
Capitals are for people
Since capitals cost nothing, spend them on readers. United States federal guidance on accessible social media puts the reason plainly. Hashtags that contain several words can present accessibility issues for people who use screen readers, because the program may treat the whole tag as one word and read it that way, and the result can be confusing or unintelligible. Its instruction is to capitalise each word of a multiword hashtag.
The same capitals help anyone reading quickly, since the eye finds word boundaries faster than it can infer them, and they prevent a second kind of accident. Join the words who represents in lower case and the string begins with a word nobody intended. Nothing but reading catches every case of that. The page prints the joined lower-case line under each built tag for that purpose, with a dot at every join, so the accident is in front of you before it is in front of anyone else. Its built-in list holds 22 words and is described on the page as incomplete, because it is.
There is one thing the tool refuses to do. It adds a capital only at a join you typed. Finding the words inside a tag that is already joined would take a dictionary and a guess, and the guess fails on names, on other languages and on exactly the unlucky strings above. How a given screen reader voices the result also depends on the reader, the voice and its settings, and no web page can hear that.
Accents get the same caution. Dropping them is optional and uses Unicode canonical decomposition only. Letters with no decomposition stay as typed, and the default is to keep every mark.
Why this page shows no popularity numbers
The older page at this address promised suggestions sorted into popularity tiers. A page that runs entirely on your device and makes no request has no usage data. Any tier it printed would be invented, and an invented number that looks like reach is worse than no number. So there are none. Broad, Niche and Ours are names you give your own groups, and they mean what you decide they mean.
More tags is not a strategy either. Each tag is a small claim about what a post is. A long tail of loosely related tags weakens every claim in it, and for someone listening instead of looking it is a long list to sit through. This essay cannot cite a correct count and distrusts any figure offered without a dated source. The limit in the tool is therefore your own setting, and it counts without cutting.
What holds up is a few small sets, kept and reused. Three groups of a handful of tags each, switched in or out per post, will outlast any list of hundreds. They can be saved as JSON or as a CSV file with one tag per record. Cells that begin with an equals sign, a plus, a minus or an at sign are prefixed with a single quote, the defence recommended against spreadsheet formula injection.
The method has edges, and they should be stated. It cannot tell you what any service accepts, links or limits today. It cannot hear a screen reader. It cannot catch every unfortunate reading. Its character tables are Unicode 18.0.0 while a browser’s own case mapping may be a version behind, so a letter added very recently can be classed correctly and still fail to change case.
Within those edges the work is simple. Know which character ends each tag. Type words, and let capitals mark where they meet. A tag is a promise that a word can be found again, kept by spelling and not by volume.
Sources
Every source consulted for this guide. Links checked October 11, 2026.
- UAX #31: Unicode Identifiers and Syntax Unicode Consortium
- UAX #29: Unicode Text Segmentation Unicode Consortium
- UAX #44: Unicode Character Database Unicode Consortium
- UAX #15: Unicode Normalization Forms Unicode Consortium
- UTS #39: Unicode Security Mechanisms Unicode Consortium
- UTS #51: Unicode Emoji Unicode Consortium
- Social Media Section508.gov, U.S. General Services Administration
- twitter-text/conformance/extract.yml at master twitter-text project on GitHub (Apache License 2.0)
- mastodon/app/models/tag.rb at main Mastodon project on GitHub (GNU AGPL 3.0)
- Hashtag Wikipedia
- RFC 4180: Common Format and MIME Type for Comma-Separated Values (CSV) Files IETF, RFC Editor
- RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format IETF, RFC Editor
- CSV Injection OWASP Foundation
- Intl.Segmenter MDN Web Docs