HTML is a standard defined by the WHATWG, an acronym for Web Hypertext Application Technology Working Group, an organization formed by people working on the most popular web browser. This means it’s basically controlled by Google, Mozilla, Apple and Microsoft.

In the past the W3C (World Wide Web Consortium) was the organism in charge of creating the HTML standard.

The control informally moved from W3C to WHATWG when it became clear that the W3C push towards XHTML was not a good idea.

If you’ve never heard of XHTML, here’s a short story. In the early 2000, we all believed the future of the Web was XML (seriously). So HTML moved from being a SGML-based authoring language to a XML markup language.

It was a big change. We had to know, and respect, more rules. Stricter rules.

Eventually browser vendors realized this was not the right path for the Web and they pushed back, creating what is now known as HTML5.

W3C did not really agree on leaving control of HTML and for years we got 2 competing standards, each one aiming to be the official one. Eventually on 28 May 2019 it was made official by W3C that the “true” HTML version was the one published by WHATWG.

I mentioned HTML5. Let me explain this little story. I know, it’s kinda confusing up to now, as with many things in life when many actors are involved, it’s also fascinating.

We had HTML version 1 in 1993. [Here’s the original RFC].

HTML 2 followed in 1995.

We got HTML 3 in January 1997, and HTML 4 in December 1997.

Busy times!

20+ years went by, we had all this XHTML thing, and eventually we are now at this HTML5 “thing”, which is not really just HTML any more.

HTML5 is a term that now defines a whole set of technologies, which includes HTML but adds a lot of APIs and standards like WebGL, SVG and more.

The key thing to understand here is this: there is no such thing (any more) as an HTML version now. It’s a living standard. Like CSS, we call it 3 but in reality it’s a bunch of independent modules developed separately. Like JavaScript, we have one new edition each year, but it does not matter much any more rather than which individual features are implemented by the engine.

Yes we call it HTML5 but HTML4 is from 1997. It’s a long time for anything, imagine for the web.

This is where the standard “lives”: [].

The HTML basics

HTML is the markup language we use to structure content that we consume on the Web.

HTML is served to the browser, in different ways.

It can be generated by a server-side application that builds it depending on the request or the session data, for example a Rails or Laravel or Django application.

Or it can be generated by a JavaScript client-side application that generates HTML on the fly.

Or, in the simplest case, it can be stored into a file, and served to the browser by a Web server.

Let’s dive into this case, although in practice it’s probably the least popular way to generate HTML, it’s still essential to know the basic building blocks.

By convention, an HTML file is saved with a .html or .htm extension.

Inside this file, we organize the content using tags.

Tags wrap the content, and each tag give a special meaning to the text it wraps.

Let’s make a few examples.

This HTML snippet creates a paragraph using the p tag:

<p>A paragraph of text</p>

This HTML snippet creates a list of items using the ul tag, which means unordered list, and the li tags, which mean list item:

  <li>First item</li>
  <li>Second item</li>
  <li>Third item</li>

When an HTML page is served by the browser, the tags are interpreted and the browser renders the elements according to the rules that define the visual appearance of them.

Some of those rules are built-in. Like how a list renders, for example. Or how a link is rendered in blue, underlined.

Some other rules are set by you with CSS.

HTML is not presentational. It’s not concerned with how things look. Instead, it’s concerned with what things mean.

It’s up to the browser to determine how things look, with the directives defined by who builds the page, with the CSS language.

Now, those 2 examples I made are HTML snippets taken outside of a page context.

An HTML page structure

Let’s make an example of a proper HTML page.

Things start with the Document Type Declaration (aka doctype), a way to tell the browser this is an HTML page, and which version of HTML we are using.

Modern HTML uses this doctype:

<!DOCTYPE html>

Then we have the html element, which has an opening and closing tag:

<!DOCTYPE html>

All tags have an opening and closing tag. Except a few self-closing tags which don’t need a closing one because they don’t contain anything in them.

The closing tag is same as the opening one, but with a /.

The html starting tag is used at the beginning of the document, right after the document type declaration.

The html ending tag is the last thing present in an HTML document.

Inside the html element we have 2 elements: head and body:

<!DOCTYPE html>

Inside head we will have tags that are essential to creating a web page, like the title, the metadata, and internal or external CSS and JavaScript. Mostly things that do not directly appear on the page, but only help the browser (or bots like the Google search bot) display it properly.

Inside body we will have the content of the page. The visible stuff.

Tags vs elements

I mentioned tags and elements. What’s the difference?

Elements have a starting tag, and closing tag.

In this case, we use the p starting and closing tags to create a p element.

<p>A paragraph of text</p>

So, an element constitutes the whole package:

  • starting tag
  • text content (and possibly other elements)
  • closing tag

If an element has no closing tag, it is only written with the starting tag, and it cannot contain any text content.

That said, I might use the tag or element term in the book meaning the same thing, except I explicitly mention starting tag or ending tag.


The starting tag of an element can have special snippets of information we can attach, called attributes.

Attributes have the key="value" syntax:

<p class="a-class">A paragraph of text</p>

You can also use single quotes, but using double quotes in HTML is a nice convention.

We can have multiple of them:

<p class="a-class" id="an-id">A paragraph of text</p>

and some attributes are boolean, meaning you only need the key:

<script defer src="file.js"></script>

The class and id attributes are two of the most common you will find used.

They have a special meaning, and they are useful both in CSS and JavaScript.

The difference between the two is that an id is unique in the context of a web page, it cannot be duplicated.

Classes, on the other hand, can appear multiple times on multiple elements.

Plus, an id is just one value. class can hold multiple values, separated by a space:

<p class="a-class another-class">A paragraph of text</p>

It’s common to use the dash - to separate words in a class value, but it’s just a convention.

Those are just 2 of the possible attributes you can have. Some attributes are only used for one tag. They are highly specialized.

Other attributes can be used in a more general way. You just saw id and class, but we have other ones too, like style which can be used to insert inline CSS rules on an element.

Case insensitive

HTML is case insensitive. Tags can be written in all caps, or lowercase. In the early days, caps were the norm. Today lowercase is the norm. It is a convention.

You usually write like this:

<p>A paragraph of text</p>

not like this:

<P>A paragraph of text</P>

White space

Pretty important. In HTML, even if you add multiple white spaces into a line, it’s collapsed by the browser’s CSS engine.

For example the rendering of this paragraph

<p>A paragraph of text</p>

is the same as this:

<p>        A paragraph of text</p>

and the same as this:

<p>A paragraph

           text          </p>

Using the white-space CSS property you can change how things behave. You can find more information on how CSS processes white space in the CSS Spec

I’d say use the syntax that makes things visually more organized and easier to read, but you can use any syntax you like.

I typically route for

<p>A paragraph of text</p>


	A paragraph of text

Nested tags should be indented with 2 or 4 characters, depending on your preference:

		A paragraph of text
		<li>A list item</li>

Note: this means that if you want to add an additional space, it can make you pretty mad. I suggest to use CSS to make more space when needed.

Note: in special cases, you can use the &nbsp; HTML entity (an acronym that means non-breaking space) - more on HTML entities later on. I think this should not be abused. CSS is always preferred to alter the visual presentation.

The document heading

The head tag contains special tags that define the document properties.

It’s always written before the body tag, right after the opening html tag:

<!DOCTYPE html>

We never use attributes on this tag. And we don’t write content in it.

It’s just a container for other tags. Inside it we can have a wide variety of tags, depending on what you need to do:

  • title
  • script
  • noscript
  • link
  • style
  • base
  • meta

The title tag

The title tag determines the page title. The title is displayed in the browser, and it’s especially important as it’s one of the key factors for Search Engines Optimization.

The script tag

This tag is used to add JavaScript into the page.

You can include it inline, using an opening tag, the JavaScript code and then the closing tag:

..some JS

Or you can load an external JavaScript file by using the src attribute:

<script src="file.js"></script>

The type attribute by default is set to text/javascript, so it’s completely optional.

There is something pretty important to know about this tag.

Sometimes this tag is used at the bottom of the page. Why? For performance reasons.

Loading scripts by default blocks the rendering of the page until the script is parsed and loaded.

Doing so, the script is loaded and executed after all the page is already parsed and loaded, giving a better experience to the user over keeping it in the head tag.

My opinion is that this is now bad practice. Let script live in the head tag.

In modern JavaScript we have an alternative, more performant than keeping the script at the bottom of the page - defer attribute:

<script defer src="file.js"></script>

This is the scenario that triggers the faster path to a fast loaded page, and a fast loaded JavaScript.

Note: the async attribute is similar, but in my opinion a worse option than defer. I describe why in details in the page

The noscript tag

This tag is used to detect when scripts are disabled in the browser.

Note: users can choose to disable JavaScript scripts in the browser settings. Or the browser might not support them by default.

It is used differently whether it’s put in the document head, or in the document body.

We’re talking about the document head now, so let’s first introduce this usage.

In this case, the noscript tag can only contain other tags:

  • link tags
  • style tags
  • meta tags

to alter the resources served by the page, or the meta information, if scripts are disabled.

In this example I set an element with the no-script-alert class to display if scripts are disabled, as it was display: none by default:

<!DOCTYPE html>
				.no-script-alert {
					display: block;


Let’s solve the other case: if put in the body, it can contain content, like paragraphs and other tags, which are rendered in the UI.

The link tag is used to set relationships between a document and other resources.

It’s mainly used to link an external CSS file to be loaded.

This element has no closing tag.


<!DOCTYPE html>
		<link href="file.css" rel="stylesheet">

The media attribute allows to load different stylesheets depending on the device capabilities:

<link href="file.css" media="screen" rel="stylesheet">
<link href="print.css" media="print" rel="stylesheet">

We can link to different resources than stylesheets.

For example we can associate an RSS feed using

<link rel="alternate" type="application/rss+xml" href="/index.xml">

We can associate a favicon using:

<link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png">

<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png">

<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png">

This tag was also used for multi-page content, to indicate the previous and next page using rel="prev" and rel="next". Mostly for Google. In 2019 Google announced it does not use this tag any more because it can find the correct page structure without it.

The style tag

This tag can be used to add styles into the document, rather than loading an external stylesheet.


.some-css {}

As with the link tag, you can use the media attribute to only use that CSS on the specified medium:

<style media="print">
.some-css {}

You can also add this tag in the document body. Speaking of this, it’s interesting the scoped attribute to only assign that CSS to the current document subtree. In other words, to avoid leaking the CSS outside of the parent element.

The base tag

This tag is used to set a base URL for all relative URLs contained in the page.

<!DOCTYPE html>
		<base href="">

The meta tag

Meta tags perform a variety of tasks and they are very, very important.

Especially for SEO.

meta elements only have the starting tag.

The most basic one is the description meta tag:

<meta name="description" content="A nice page">

This might be used by Google to generate the page description in its result pages, if it finds it better describes the page than the on-page content (don’t ask me how).

The charset meta tag is used to set the page character encoding. utf-8 in most cases:

<meta charset="utf-8">

The robots meta tag instructs the Search Engine bots whether to index a page or not:

<meta name="robots" content="noindex">

Or if they should follow links or not:

<meta name="robots" content="nofollow">

You can set nofollow on individual links, too. This is how you can set nofollow globally.

You can combine them:

<meta name="robots" content="noindex, nofollow">

The default behavior is index, follow.

You can use other properties, including nosnippet, noarchive, noimageindex and more.

You can also just tell Google instead of targeting all search engines:

<meta name="googlebot" content="noindex, nofollow">

and other search engines might have their own meta tag, too.

Speaking of which, we can tell Google to disable some features. This prevents the translate functionality in the search engine results:

<meta name="google" content="notranslate">

The viewport meta tag is used to tell the browser to set the page width depending on the device width.

<meta name="viewport" content="width=device-width, initial-scale=1">

See more on this tag.

Another rather popular meta tag is the http-equiv="refresh" one. This line tells the browser to wait 3 seconds, then redirect to that other page:

<meta http-equiv="refresh" content="3;url=">

Using 0 instead of 3 will redirect as soon as possible.

This is not a full reference, other less used meta tags exist.

After this document heading introduction, we can start diving into the document body.

The document body

After the closing head tag, we can only have one thing in an HTML document: the body element.

<!DOCTYPE html>

Just like the head and html tags, we can only have one body tag in one page.

Inside the body tag we have all the tags that define the content of the page.

Technically, the start and ending tags are optional. But I consider it a good practice to add them. Just for clarity.

In the next chapters we’ll define the variety of tags you can use inside the page body.

But before, we must introduce a difference between block elements and inline elements.

Block elements vs inline elements

Visual elements, the ones defined in the page body, can be generally classified in 2 categories:

  • block elements (p, div, heading elements, lists and list items, …)
  • inline elements (a, span, img, …)

What is the difference?

Block elements, when positioned in the page, do not allow other elements next to them. To the left, or to the right.

Inline elements instead can sit next to other inline elements.

The difference also lies in the visual properties we can edit using CSS. We can alter the width/height, margin, padding and border or block elements. We can’t do that for inline elements.

Note that using CSS we can change the default for each element, setting a p tag to be inline, for example, or a span to be a block element.

Another difference is that inline elements can be contained in block elements. The reverse is not true.

Some block elements can contain other block elements, but it depends. The p tag for example does not allow such option.

Download my free HTML book!

Did you know?

I have 7 premium training programs that will transform you, quickly, into an excellent Node/React/JS/Vue/Next.js/Svelte developer. Choose your own adventure. Practical lessons to learn the 80% of the things that you need to know, in 20% of the time!

⬇️ ⬇️ ⬇️

Check out all my courses now!

⬆️ ⬆️ ⬆️