Block vs Inline Elements
Every HTML element has a default rendering behavior described by its CSS display value: most are either block-level or inline-level. Understanding the difference explains a lot of "why doesn't my layout look right" moments, and it explains why certain elements can't be nested inside others.
Block-Level Elements
Block-level elements start on a new line and stretch to fill the width of their parent container by default. They can contain other block-level elements as well as inline elements, and they respect top/bottom margin.
Element | Typical use |
|---|---|
| Generic block container, no inherent meaning |
| A paragraph of text |
| Section headings |
| Lists and list items |
| Semantic page regions |
| Tabular data, form containers |
block-example.html
<div> <h2>Article Title</h2> <p>Each of these starts on its own line and fills the available width.</p> </div>
Inline-Level Elements
Inline-level elements flow within a line of text, only taking up as much width as their content needs. They don't force a line break before or after themselves, and top/bottom margin has no effect on layout (though left/right margin and padding do apply).
Element | Typical use |
|---|---|
| Generic inline container, no inherent meaning |
| Hyperlinks |
| Emphasis within a sentence |
| An image embedded in a line of text or on its own |
| Inline semantic text markers |
| Small interactive controls that flow with text |
inline-example.html
<p> Read the <a href="/docs">docs</a>, then check the <strong>quick start</strong> guide. Each of these stays <em>within</em> the flow of this single line of text. </p>
Side by Side
Behavior | Block-level | Inline-level |
|---|---|---|
Starts on a new line | Yes | No — flows with surrounding text |
Width | Fills parent by default | Only as wide as its content |
Height | Respects height/width you set | Height mostly follows content |
Top/bottom margin | Applied | Ignored for layout purposes |
Can contain block children | Usually yes | No — only other inline content |
Why You Can't Nest Block Inside Inline
Semantically, an inline element represents a run of text within a larger flow — it doesn't make sense for a "run of text" to contain a whole section, a list, or a table. The HTML content model reflects this: inline elements are only allowed to contain "phrasing content" (other inline-level things), not block-level elements.
<a> could only wrap phrasing content, so something like <a><div>...</div></a> was invalid. HTML5 relaxed this specifically for <a>, which may now wrap block-level content when the whole block should be clickable. Most other inline elements (<span>, <strong>, <em>) still may not.anchor-wrapping-block.html
<!-- Valid in HTML5: <a> may wrap block-level content --> <a href="/product/42" class="card-link"> <h3>Wireless Keyboard</h3> <p>Compact, backlit, and rechargeable.</p> </a> <!-- Invalid: <span> may not contain block-level content --> <span> <div>This breaks the content model</div> </span>
CSS Can Override the Default display
"Block" and "inline" describe the default rendering behavior baked into the browser's built-in stylesheet — they are not fixed, unchangeable categories. Any element's display property can be overridden with CSS.
override-display.css
/* Make an inline <span> behave like a block box */
span.callout {
display: block;
}
/* Make a block-level <li> flow inline, e.g. for a horizontal nav */
nav li {
display: inline-block;
}display: block on a <span> changes how it's rendered, but it doesn't change what HTML considers valid to nest inside it — the parser still enforces the original content model based on the element type, not its computed CSS display value.inline-block flows with surrounding text like an inline element but respects width, height, and vertical margin like a block element — useful for things like custom buttons or nav items that need to sit in a row but also need box-model control.Block-level elements stack vertically and fill their parent's width by default.
Inline-level elements flow horizontally within a line of text.
The HTML content model restricts what can nest inside what, independent of CSS.
CSS
displaycan change how something renders without changing what's valid to nest inside it.
Modern display Values Beyond Block and Inline
The block/inline distinction predates CSS3's richer layout models. flex and grid create entirely new layout contexts for their children, while inline-flex and inline-grid do the same but flow the container itself inline, like an inline-level box.
display value | Container behaves like | Children laid out by |
|---|---|---|
| Block-level box | Flexbox algorithm |
| Inline-level box | Flexbox algorithm |
| Block-level box | Grid algorithm |
| Inline-level box | Grid algorithm |
flex-container.css
.toolbar {
display: flex; /* the toolbar itself is a block-level box */
gap: 8px;
}Semantic Elements Are Still Block by Default
HTML5's newer sectioning elements — <section>, <article>, <header>, <footer>, <nav>, <aside>, <main> — are all block-level by default in the browser's built-in stylesheet, even though "semantic" and "block" describe unrelated properties. Don't assume an element is inline just because it's new or unfamiliar; check its default display value.
Category | Examples | Default display |
|---|---|---|
Sectioning |
| block |
Grouping |
| block |
Text-level semantics |
| inline |
Table-related |
| table, table-row, table-cell |
A Practical Debugging Tip
When a layout doesn't behave as expected — an element refuses to accept a width, or two elements won't sit side by side — checking its computed display value in browser dev tools is often the fastest way to understand why.
debug-with-devtools.css
/* In DevTools > Computed tab, look for: */ display: inline; /* width/height on this element will be ignored */
<table>, <tr>, and <td> use table-specific display values (table, table-row, table-cell) that don't fit neatly into "block" or "inline" — they follow their own layout rules unless overridden with CSS.