How should I name IDs in HTML?

By Brian Tomasik

First published: . Last nontrivial update: .

Summary

This page explores the question of how I should write ID values in HTML, especially the IDs of section headings. My current convention is to use a style like this:

<h2 id="train_or_plane">Should I take a train or plane?</h2>

Contents

Introduction

The most common reason I use HTML IDs is for a table of contents within an article. I give an ID to each h2 , h3 , etc section heading, and then I build the table of contents automatically to link to those sections.

Links to an ID within the same page are sometimes called "page jumps" and look like this:

<a href="#a_later_section">this is a link to a later section</a>

The a_later_section string is called a "fragment identifier".

From roughly 2014 to 2019, my table of contents was created automatically using the "Table of Contents Plus" WordPress plugin by Michael Tran. This plugin created an ID for a heading automatically based on the heading's text. Here's an example heading with an auto-generated ID:

<h2><span id="Update_Feb_2015_You_are_all_your_copies">Update, Feb. 2015: You are all your copies</span></h2>

From 2019 onward, I moved away from WordPress and began writing my heading IDs by hand. I wanted to think carefully about what convention to use for naming those heading IDs so that I could use that convention consistently going forward, rather than switching unthinkingly between different conventions. This page describes my deliberation regarding what convention to use.

My deviations from Tran's convention

One option could have been to keep using the exact same convention as Tran's plugin used, for the sake of consistency. However, I didn't want to do this for several reasons.

One obvious downside of Tran's convention is verbosity: it unnecessarily uses a span element to hold the ID, when the ID could just be put on the h2 element directly, like this:

<h2 id="Update_Feb_2015_You_are_all_your_copies">Update, Feb. 2015: You are all your copies</h2>

I haven't looked into the matter, but I would guess that maybe Tran chose to add the span element in case the h2 element already had an ID? But when I'm setting IDs by hand, I can see for myself that the h2 element will only have one ID.

Tran's plugin also sets the ID automatically based on the heading text. While this has the virtue of saving the page author a bit of time, because the heading ID doesn't have to be written out by hand, it also has a downside: if you later change the heading's text, the ID also changes, which means that old links to that ID won't work. Sometimes breaking links to sections of an article is inevitable, such as if you remove a given section entirely. But if you only change a heading's text by a word or two, it'd be nice to be able to keep the ID the same as before to avoid breaking any inbound links, including links on other people's websites that you can't change. When you set the ID by hand, you can know that it won't change until you deliberately change it.

If you want heading IDs to remain constant even if the heading text changes slightly, then it won't necessarily be the case that the ID perfectly matches the heading text. For example, suppose the heading text

Update, Feb. 2015: You are all your copies

were rewritten to

Update from 2015: You are all of your copies at once

The heading ID would nonetheless remain fixed as Update_Feb_2015_You_are_all_your_copies . Since there's not necessarily a perfect relationship between the heading text and its ID, this opens the door to having the heading ID be different from the heading text even from the outset. For example, maybe we could write the heading ID more concisely like this:

<h2 id="all_your_copies">Update, Feb. 2015: You are all your copies</h2>

This concision is nice when you're typing out IDs by hand. It also implies a shorter URL if someone shares a link to a section of your article.

Because heading IDs are chosen somewhat independently of the heading text, I also think it makes sense for the heading ID to start with a lowercase letter, in contrast to the section text, which starts with an uppercase letter. This lowercase-versus-uppercase distinction makes it more clear that the ID is just an abbreviation and isn't strictly tied to match the heading text.

Dashes versus underscores

Another decision I had to make was whether to keep using underscores to separate words in the ID or whether to use dashes instead. There's apparently no consensus on this issue, and there are many arguments on both sides (Stack Overflow "What is the standard naming convention ..."). I consider Wikipedia a good model to follow, and that site uses underscores in ID names, although Wikipedia is also one of the rare sites to use underscores in its main URLs too.

One argument in favor of dashes is that URLs typically use dashes, so having the fragment identifier also use dashes would improve consistency in the URL, like this:

https://briantomasik.com/naming-IDs/#dashes-versus-underscores

On the other hand, you could also argue in favor of underscores because they'd more clearly show the distinction between the main URL versus the fragment identifier:

https://briantomasik.com/naming-IDs/#dashes_versus_underscores

Ultimately the argument that tipped the balance for me in favor of underscores had to do with ease of copying the ID in a text editor. In many text editors, if you double-click on an ID separated by underscores, the whole ID is highlighted at once, which makes copying it easy. In contrast, if the parts are separated by dashes, double-clicking only highlights one part. In that case you have to expend a bit more effort to select the entire ID.