title
Defines the document title shown in browser tabs, bookmarks, history, search results, and screen reader announcements.
Description
The <title> element defines the document's name. It is the only required element inside <head> and serves as the primary identifier for the page across every browser surface: tabs, bookmarks, history, task switchers, and search engine results. Screen readers announce it first when a user lands on a page.
Beyond static identification, document.title is a live, writable property. This makes the browser tab a zero-UI status channel: prepend an emoji for state, a count for notifications, or cycle through animation frames for a loading indicator — all without touching the visible page.
Basic Example
<title>Page Title</title>
Format Patterns
VB uses the Page | Site convention. The page-specific part comes first so users scanning many tabs see the distinguishing content before it gets truncated.
<title>Page Title | Site Name</title>
| Pattern | Example | When to Use |
|---|---|---|
Page | Site |
Pricing | Acme Corp | Default for most pages. VB's head partial generates this automatically. |
Site |
Acme Corp | Homepage only, where the page name would be redundant. |
(n) Page | Site |
(3) Inbox | My App | Notification count. The number draws the eye in a row of tabs. |
Emoji Page | Site |
✉️ New message | My App | Status indicator. Works without JavaScript if the state is known at render time. |
Keep titles under 60 characters. Search engines truncate around that point, and shorter titles are easier for screen reader users to parse.
Static Emoji Status
The simplest form of tab status: prepend an emoji to the title in your HTML. No JavaScript required. This works for any state known at render time — connection status, build results, form state.
<!-- Connection status --><title>🟢 Connected | Dashboard</title><title>🔴 Offline | Dashboard</title> <!-- Activity status --><title>⏳ Uploading... | Files</title><title>✅ Upload complete | Files</title> <!-- Notification types --><title>📬 (3) new | Inbox</title><title>⚠️ Action required | Settings</title>
Choose emoji that are visually distinct at small sizes. Colored circles, warning signs, and mail icons work well. Avoid emoji that look similar across platforms or require fine detail to distinguish.
Dynamic Title Updates
document.title is writable and updates the browser tab immediately. Use it for SPA route changes and notification badges.
<script>// Save the original title onceconst originalTitle = document.title; // Update on route change (SPA)function setPageTitle(pageName) { document.title = pageName + ' | My App';} // Add a notification countfunction setNotificationCount(count) { document.title = count > 0 ? `(${count}) ${originalTitle}` : originalTitle;}</script>
<title>(3) Inbox | My App</title>
Animated Spinners
Cycle through an emoji sequence with setInterval to create a loading spinner directly in the browser tab. This is a progressive enhancement — the tab title remains readable text with a rotating prefix.
<script>// Emoji sequences that animate well in browser tabsconst spinners = { moon: ['🌑','🌒','🌓','🌔','🌕','🌖','🌗','🌘'], clock: ['🕛','🕐','🕑','🕒','🕓','🕔','🕕','🕖','🕗','🕘','🕙','🕚'], earth: ['🌍','🌎','🌏'], braille: ['\u280B','\u2819','\u2839','\u2838','\u283C','\u2834','\u2826','\u2827','\u2807','\u280F'], dots: ['\u22C5','\u2219','\u25CF','\u2219'],}; let timer = null;const originalTitle = document.title; function startSpinner(type = 'moon') { const frames = spinners[type]; let i = 0; timer = setInterval(() => { document.title = frames[i++ % frames.length] + ' ' + originalTitle; }, 150);} function stopSpinner() { clearInterval(timer); timer = null; document.title = originalTitle;} // Usage: show spinner during a fetchstartSpinner('clock');fetch('/api/data') .then(res => res.json()) .then(data => { stopSpinner(); // ... use data }) .catch(() => { stopSpinner(); document.title = '\u26A0\uFE0F ' + originalTitle; });</script>
Spinner Sequences
These sequences animate smoothly in browser tabs. Choose based on the mood you want:
Braille dot spinners are monochrome and compact — they work best when you want a subtle, terminal-style indicator. Moon and clock emoji are more playful and visible at a glance.</p> <h3>Performance</h3> <p>Pause the spinner when the tab is hidden. The animation is invisible anyway, and clearing the interval avoids unnecessary work:</p> <code-block language="javascript" label="Visibility-aware spinner" data-escape><script>// Pause spinner when tab is hidden (saves CPU)document.addEventListener('visibilitychange', () => { if (document.hidden) { stopSpinner(); } else if (isLoading) { startSpinner(); }});</script>
document.title updates are not throttled by browsers (unlike history.pushState, which Chrome and Safari limit to ~100 calls per 30 seconds). An interval of 100–200ms produces smooth animation without measurable overhead.
Badging API
The Badging API is the platform-native way to show a numeric badge on an installed PWA's icon in the dock or taskbar. It complements title-based indicators but does not replace them — the Badging API only works for installed web apps, while title updates work in every browser tab.
<script>// Modern Badging API (installed PWAs only)// Sets a numeric badge on the app icon in the dock/taskbar // Show unread countnavigator.setAppBadge(5); // Show flag-only badge (no number)navigator.setAppBadge(); // Clear badgenavigator.clearAppBadge(); // Progressive enhancement: badge API + title fallbackfunction setBadge(count) { // Update the tab title (works everywhere) const base = document.title.replace(/^\(\d+\)\s*/, ''); document.title = count > 0 ? `(${count}) ${base}` : base; // Also set the app badge if available (installed PWAs) if ('setAppBadge' in navigator) { count > 0 ? navigator.setAppBadge(count) : navigator.clearAppBadge(); }}</script>
| Approach | Where It Shows | Requires |
|---|---|---|
document.title |
Browser tab, task switcher, history | Nothing (works everywhere) |
navigator.setAppBadge() |
App icon in dock/taskbar/home screen | PWA install, HTTPS, browser support |
Use both together for the best coverage. The title fallback ensures uninstalled users still see the notification count.
Attributes
The <title> element has no element-specific attributes. It supports global attributes, though in practice none are commonly used on it.
Its content model is text only — no child elements, no HTML tags. The text between the opening and closing tags becomes the document's title.
Accessibility
- First thing announced: Screen readers read the page title before any other content. It is the primary way a user identifies which page they have landed on.
- Unique per page: Every page must have a distinct title. Duplicate titles across pages make navigation impossible for screen reader users who rely on title differences to distinguish tabs and history entries.
- Page-specific content first: The
Page | Siteformat puts the distinguishing content first. A screen reader user scanning multiple tabs hears the unique part immediately rather than hearing the site name repeated for every tab. - Emoji are announced: Screen readers read emoji by their Unicode name.
🕐is announced as "one o'clock,"🌑as "new moon." This is meaningful but can be verbose — avoid stacking multiple emoji in the title. - Dynamic updates are announced: Some screen readers announce title changes via live region behavior. Rapidly cycling a spinner may cause announcement storms. Use
aria-liveregions for important status messages rather than relying on title changes alone. - Keep it concise: Aim for under 60 characters. Long titles are tedious to listen to and get truncated in visual UI.
- Avoid keyword stuffing: Titles like "Shoes | Buy Shoes | Best Shoes | Shoe Store" are frustrating for everyone and actively harmful for screen reader users who must listen to the entire string.
SEO
- Search engines display roughly 50–60 characters of the title in results. Put the most important words first.
- The title should match or closely reflect the page's
<h1>heading. Mismatches between the tab title and the visible heading confuse both users and search engines. - Each page needs a unique title. Duplicate titles signal duplicate content to search crawlers.
- The
<title>and<meta name="description">work as a pair — the title is the headline, the description is the summary. Make sure they complement each other. - Dynamic emoji prefixes and notification counts do not affect SEO. Search engines index the server-rendered title, not JavaScript-modified values.
Browser Behavior
| Surface | What Shows |
|---|---|
| Tab bar | Title text, truncated to fit. Emoji render in color on macOS/iOS, monochrome on some Windows versions. |
| Bookmarks | Title at the time of bookmarking. Does not update with document.title changes. |
| History | Title at the time the history entry was created. SPA route changes should update document.title before calling history.pushState(). |
| Task switcher | OS-level alt-tab and task views show the current document.title. |
| Search results | Server-rendered title from the HTML source. JavaScript changes are not indexed. |
| Social previews | og:title takes precedence. Falls back to <title> if no OG tag is present. |
Related
<head>— Parent element; VB's head partial generates the title from frontmatter<meta>—descriptionmeta pairs with the title for search results<link>— Canonical URL and favicon, which appear alongside the title in tabs<html>— Thelangattribute on the root element affects how screen readers pronounce the title