Question
I am building a web application where the main content area should fill the remaining height of the viewport.
The page contains:
- a
headerarea with a logo and account information - a
contentarea that should stretch from the bottom of the header to the bottom of the screen
The header can have an arbitrary height, so I cannot hard-code its size.
Right now, I am using a table-based layout like this:
<table id="page">
<tr>
<td id="tdheader">
<div id="header">...</div>
</td>
</tr>
<tr>
<td id="tdcontent">
<div id="content">...</div>
</td>
</tr>
</table>
#page {
height: 100%;
width: 100%;
}
#tdcontent {
height: 100%;
}
#content {
overflow: auto; /* or overflow: hidden; */
}
This makes the page fill the full height, and scrolling is not required for the overall page.
I want to achieve the same effect without using a table for layout.
There is one additional requirement: elements inside the content div may also use percentage heights. For example:
- one child at
height: 100%should fill the content area - two children at
height: 50%should split the content area evenly
For example, if the header takes up 20% of the screen height, then a table inside #content with height: 50% should end up using 40% of the viewport height.
Is there a CSS-based way to do this with normal div elements instead of a table layout?
Short Answer
By the end of this page, you will understand how to make a layout where a header keeps its natural height and the content area fills the remaining viewport height. You will also learn how to make percentage heights work correctly inside that content area using modern CSS, especially Flexbox.
Concept
The core concept here is creating a parent container with a known height, then letting one child grow to fill the remaining space.
In CSS, percentage heights only work when the browser can determine the height of the parent. That is why table layouts often seemed to solve this problem in older code: table cells naturally participate in height calculations.
With normal div elements, the modern solution is usually Flexbox.
The typical idea is:
- Make the page container as tall as the viewport.
- Let the header take its natural height.
- Let the content area grow to fill the rest.
- Give the content area a definite computed height so percentage-based children can size themselves correctly.
A common pattern looks like this:
bodyor a wrapper getsmin-height: 100vhorheight: 100vh- wrapper gets
display: flex - wrapper uses
flex-direction: column - header stays natural size
- content gets
flex: 1
Why this matters in real programming:
- app layouts often have a top bar and a main content panel
- dashboards need panels that fill the remaining screen area
- scrollable content regions are common in admin tools and web apps
- embedded tables, sidebars, editors, and split panes often depend on percentage heights
The most important rule to remember is:
A child cannot reliably use height: 100% unless its parent has a resolved height.
So the real problem is not just “make this div taller.” The real problem is “create a layout chain where heights are explicitly defined or computable.”
Mental Model
Think of the page like a vertical stack inside a fixed-height container.
- The viewport is the full container.
- The header is a box that takes however much space it needs.
- The content area is a flexible box that expands to occupy everything left over.
Imagine packing a suitcase:
- first, put in the header item
- then the remaining space automatically belongs to the content item
If the suitcase itself has a known height, then anything inside the content area can also measure itself as a percentage of that remaining space.
So the key mental model is:
known outer height -> flexible remaining area -> percentage-sized inner children
Syntax and Examples
Basic Flexbox layout
<div class="page">
<header class="header">
<h1>Logo</h1>
<p>Account info</p>
</header>
<main class="content">
<div class="panel">Main content</div>
</main>
</div>
html, body {
height: 100%;
margin: 0;
}
.page {
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
padding: 16px;
background: #e5e7eb;
}
.content {
flex: 1;
overflow: auto;
background: #f9fafb;
}
.panel {
height: 100%;
background: #dbeafe;
}
What this does
.pageis exactly as tall as the viewport..headeruses its natural height..contentgrows to fill all remaining space..panelcan useheight: 100%because.contenthas a resolved size through flex layout.
Example with two equal sections inside content
<div class="page">
<header class="header">Header</header>
<main class="content">
<div class="top-half">Top</div>
<div class="bottom-half">Bottom</div>
</main>
</div>
html, body {
height: 100%;
margin: 0;
}
.page {
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
padding: 20px;
background: #ddd;
}
.content {
flex: 1;
display: flex;
flex-direction: column;
}
.top-half,
.bottom-half {
height: 50%;
}
.top-half {
background: #c7d2fe;
}
.bottom-half {
background: #bfdbfe;
}
This works because the .content element has a definite height: it is the remaining viewport height after the header is laid out.
A practical improvement: min-height: 0
In nested flex layouts, add min-height: 0 to the growing area when overflow should work correctly.
.content {
flex: 1;
min-height: 0;
overflow: auto;
}
This is especially useful when content should scroll inside the panel instead of forcing the whole page to grow.
Step by Step Execution
Consider this example:
<div class="page">
<header class="header">My Header</header>
<main class="content">
<div class="inner">I fill the remaining height</div>
</main>
</div>
html, body {
height: 100%;
margin: 0;
}
.page {
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
padding: 30px;
}
.content {
flex: 1;
}
.inner {
height: 100%;
}
Step by step
-
html, body { height: 100%; }- This allows full-page layouts to inherit height correctly.
-
.page { height: 100vh; }- The page wrapper becomes exactly the height of the viewport.
-
.page { display: flex; flex-direction: column; }- Its children are arranged vertically.
-
.headeris rendered first- It takes only the height needed by its contents and padding.
-
.content { flex: 1; }- Flexbox gives all remaining vertical space to
.content.
- Flexbox gives all remaining vertical space to
-
.inner { height: 100%; }- Since
.contentnow has a computed height,.innercan fill it.
- Since
Example with numbers
Suppose the viewport height is 1000px.
- header height becomes
200px - remaining space is
800px .contentgets800px.innerwithheight: 100%becomes800px
If two children inside .content each have height: 50%, each becomes 400px.
That matches the requirement exactly.
Real World Use Cases
This layout pattern appears in many real applications:
Web app shells
A typical app has:
- top navigation or header
- main content area below it
The main content should fill all remaining space.
Admin dashboards
Dashboards often contain:
- a fixed top bar
- a chart/table area that should expand to the rest of the screen
Split panels and editors
Code editors, chat tools, and project management apps often need:
- a header
- a content region
- internal panels with percentage or flexible heights
Scrollable data tables
A page may need:
- header with filters and actions
- content area containing a table
- the table region should scroll while the header stays visible
Mobile and desktop responsive layouts
Modern responsive UIs often avoid old table layouts and instead use Flexbox because it adapts better across screen sizes.
Real Codebase Usage
In real projects, developers usually solve this with a small set of repeatable layout patterns.
Common layout wrapper pattern
.app {
height: 100vh;
display: flex;
flex-direction: column;
}
.app-main {
flex: 1;
min-height: 0;
}
This creates a reusable application shell.
Scroll only the content area
.app-main {
flex: 1;
min-height: 0;
overflow: auto;
}
This keeps the header visible while only the main region scrolls.
Nested flex layouts
A content area often contains more flexible sections:
.content {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.toolbar {
padding: 12px;
}
.results {
flex: 1;
min-height: 0;
overflow: auto;
}
This is common in dashboards, tables, search screens, and admin panels.
Guarding percentage heights
If a child must use height: 100%, developers make sure every parent in the chain has a computable height.
For example:
- viewport height on outer wrapper
flex: 1on content regionheight: 100%only after that is true
Why table layouts are avoided
Developers generally avoid using HTML tables for page structure because:
- tables are meant for tabular data
- they are harder to maintain as layouts grow
- responsive behavior is less flexible
- Flexbox and Grid are designed specifically for layout
Common Mistakes
1. Using height: 100% without giving the parent a height
Broken example:
.content {
height: 100%;
}
If the parent does not have a defined or computed height, this may not work.
Fix
Give the parent a real height source:
.page {
height: 100vh;
}
Then let the content grow with flex:
.page {
display: flex;
flex-direction: column;
}
.content {
flex: 1;
}
2. Forgetting to set html and body height when needed
Broken example:
body {
height: 100%;
}
If html does not also have height, the percentage chain may fail.
Fix
html, body {
height: 100%;
margin: 0;
}
3. Expecting percentage heights to work through undefined ancestors
Broken example:
<div class="outer">
<div class="content">
<div class="child"></div>
</div>
</div>
.child {
height: 100%;
}
If .outer and .content do not have computable heights, .child cannot resolve 100% properly.
4. Using tables for layout instead of data
Tables can appear to solve the height problem, but they are the wrong semantic tool for page layout.
Use:
- Flexbox for one-dimensional layouts
- Grid for two-dimensional layouts
5. Missing min-height: 0 in nested flex layouts
A common issue is that the scrollable child refuses to shrink.
Fix
.content {
flex: 1;
min-height: 0;
overflow: auto;
}
6. Hard-coding the header height unnecessarily
Broken idea:
.content {
height: calc(100vh - 120px);
}
This only works if the header is always 120px tall.
If the header height is dynamic, Flexbox is usually better.
Comparisons
| Approach | Best for | Handles variable header height well? | Good for percentage children? | Notes |
|---|---|---|---|---|
| HTML table layout | Old layout techniques | Yes | Yes | Works, but not recommended for page layout |
height: calc(100vh - Xpx) | Fixed-size header | No | Yes | Good only when header height is known |
| Flexbox | Vertical or horizontal app layouts | Yes | Yes | Best fit for this problem |
| CSS Grid | More complex page layouts | Yes | Yes | Great when you need rows and columns |
Flexbox vs calc()
- Use Flexbox when the header height is dynamic.
- Use
calc()when the header height is fixed and known.
Example with calc():
.content {
height: calc(100vh - 80px);
}
This is simple, but it fails if the header changes height.
Flexbox vs Grid
- Flexbox is ideal when the layout is mainly one-dimensional, like a vertical stack.
- Grid is better when you need both rows and columns.
A Grid version could look like this:
.page {
height: 100vh;
display: grid;
grid-template-rows: auto 1fr;
}
This also works well for a natural-height header plus remaining-space content.
Cheat Sheet
html, body {
height: 100%;
margin: 0;
}
.page {
height: 100vh;
display: flex;
flex-direction: column;
}
.header {
/* natural height */
}
.content {
flex: 1;
min-height: 0;
overflow: auto;
}
Key rules
height: 100%needs a parent with a known height.flex: 1makes an element fill remaining space.- Use
flex-direction: columnfor header-above-content layouts. - Use
min-height: 0in nested flex layouts when scrolling is needed. - Prefer Flexbox or Grid over table-based layout.
Useful alternatives
Fixed header height:
.content {
height: calc(100vh - 80px);
}
Grid version:
.page {
height: 100vh;
display: grid;
grid-template-rows: auto 1fr;
}
Common debugging checklist
- Did you set
html, bodyheight? - Does the outer wrapper have
height: 100vhor equivalent? - Is the content area using
flex: 1? - Are percentage-height children inside a parent with a computed height?
- Do you need
min-height: 0for overflow to behave correctly?
FAQ
Why does height: 100% not work on my div?
Because the browser needs a defined parent height to calculate 100%. If the parent height is auto, the percentage may not resolve as expected.
What is the best modern way to fill the remaining screen height?
Usually Flexbox. Set the page container to display: flex, use flex-direction: column, and give the content area flex: 1.
Can I do this if the header height changes dynamically?
Yes. Flexbox and Grid both handle variable header heights well.
Should I use a table for page layout?
No, not for general page structure. Tables should be used for tabular data. Flexbox or Grid are better layout tools.
How do I make children inside the content area use percentage heights?
Make sure the content area itself has a computable height, typically by placing it inside a flex or grid layout that gives it remaining space.
When should I use calc(100vh - headerHeight)?
Use it only when the header height is fixed and known. If the header height can change, Flexbox is usually better.
Why is my inner panel not scrolling inside a flex layout?
You may need min-height: 0 on the flex item that should shrink and scroll.
Can CSS Grid solve the same problem?
Yes. A common pattern is grid-template-rows: auto 1fr, where the first row is the header and the second row fills the rest.
Mini Project
Description
Build a simple application shell with a variable-height header and a content area that fills the rest of the viewport. Inside the content area, create two stacked panels that each take up half of the available height. This demonstrates how remaining-height layouts and percentage heights work together in a real interface.
Goal
Create a full-height page where the header uses natural height and the content area fills the rest of the screen, with inner sections sized using percentages.
Requirements
- Create a page wrapper that fills the viewport height.
- Add a header whose height is determined by its content.
- Add a content area that fills the remaining height.
- Inside the content area, add two sections that each use 50% of the content height.
- Make the lower section scroll if its content becomes too large.
Keep learning
Related questions
Angular ngClass Conditional Class Binding Explained
Learn how to use Angular ngClass for conditional classes, fix common binding mistakes, and understand why this template error happens.
CSS :not() Selector for Elements Without a Class or Attribute
Learn how to use the CSS :not() selector to target elements that do not have a specific class or attribute, with examples and common pitfalls.
CSS Font Scaling Relative to Container Size: %, em, rem, vw, and Responsive Text
Learn how CSS font scaling really works and how to make text responsive using %, em, rem, vw, clamp(), and media queries.