diff --git a/include/litehtml/table.h b/include/litehtml/table.h
index 53ef837..1c7b0b7 100644
--- a/include/litehtml/table.h
+++ b/include/litehtml/table.h
@@ -210,12 +210,15 @@ namespace litehtml
 		rows					m_cells;
 		table_column::vector	m_columns;
 		table_row::vector		m_rows;
+		elements_vector			m_captions;
+		int						m_captions_height;
 	public:
 
 		table_grid()
 		{
 			m_rows_count	= 0;
 			m_cols_count	= 0;
+			m_captions_height = 0;
 		}
 
 		void			clear();
@@ -226,9 +229,13 @@ namespace litehtml
 		table_cell*		cell(int t_col, int t_row);
 		table_column&	column(int c)	{ return m_columns[c];	}
 		table_row&		row(int r)		{ return m_rows[r];		}
+		elements_vector& captions()		{ return m_captions; }
 
-		int				rows_count()	{ return m_rows_count;	}
-		int				cols_count()	{ return m_cols_count;	}
+		int				rows_count() const	{ return m_rows_count;	}
+		int				cols_count() const	{ return m_cols_count; }
+
+		void			captions_height(int height) { m_captions_height = height; }
+		int				captions_height() const { return m_captions_height; }
 
 		void			distribute_max_width(int width, int start, int end);
 		void			distribute_min_width(int width, int start, int end);
diff --git a/src/document.cpp b/src/document.cpp
index f67623d..8da0d9c 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -904,11 +904,14 @@ void litehtml::document::fix_table_children(element::ptr& el_ptr, style_display
 		{
 			if (!(*cur_iter)->is_white_space() || ((*cur_iter)->is_white_space() && !tmp.empty()))
 			{
-				if (tmp.empty())
+				if (disp != display_table_row_group || (*cur_iter)->get_display() != display_table_caption)
 				{
-					first_iter = cur_iter;
+					if (tmp.empty())
+					{
+						first_iter = cur_iter;
+					}
+					tmp.push_back((*cur_iter));
 				}
-				tmp.push_back((*cur_iter));
 			}
 			cur_iter++;
 		}
diff --git a/src/el_table.cpp b/src/el_table.cpp
index 06d77be..7a45671 100644
--- a/src/el_table.cpp
+++ b/src/el_table.cpp
@@ -21,7 +21,10 @@ litehtml::el_table::~el_table()
 bool litehtml::el_table::appendChild(const litehtml::element::ptr& el)
 {
 	if(!el)	return false;
-	if(!t_strcmp(el->get_tagName(), _t("tbody")) || !t_strcmp(el->get_tagName(), _t("thead")) || !t_strcmp(el->get_tagName(), _t("tfoot")))
+	if( !t_strcmp(el->get_tagName(), _t("tbody")) || 
+		!t_strcmp(el->get_tagName(), _t("thead")) || 
+		!t_strcmp(el->get_tagName(), _t("tfoot")) ||
+		!t_strcmp(el->get_tagName(), _t("caption")))
 	{
 		return html_tag::appendChild(el);
 	}
diff --git a/src/html_tag.cpp b/src/html_tag.cpp
index bf20371..4d1be32 100644
--- a/src/html_tag.cpp
+++ b/src/html_tag.cpp
@@ -583,6 +583,14 @@ void litehtml::html_tag::init()
 			row = row_iter.next(false);
 		}
 
+		for (auto& el : m_children)
+		{
+			if (el->get_display() == display_table_caption)
+			{
+				m_grid->captions().push_back(el);
+			}
+		}
+
 		m_grid->finish();
 	}
 
@@ -2045,6 +2053,13 @@ void litehtml::html_tag::draw_background( uint_ptr hdc, int x, int y, const posi
 	{
 		if(el_pos.does_intersect(clip))
 		{
+			if (m_grid)
+			{
+				int captions_height = m_grid->captions_height();
+				pos.y += captions_height;
+				pos.height -= captions_height;
+			}
+
 			const background* bg = get_background();
 			if(bg)
 			{
@@ -4610,13 +4625,46 @@ int litehtml::html_tag::render_table(int x, int y, int max_width, bool /*second_
 
 	m_pos.width = table_width;
 
+	// Render table captions
+	// Table border doesn't round the caption so we have to start caption in the border position
+	int captions_height = -border_top();
+
+	for (auto& caption : m_grid->captions())
+	{
+		caption->render(-border_left(), captions_height, table_width + border_left() + border_right());
+		captions_height += caption->height();
+	}
+
+	if (captions_height)
+	{
+		// Add border height to get the top of cells
+		captions_height += border_top();
+
+		// Save caption height for draw_background
+		m_grid->captions_height(captions_height);
+
+		// Move table cells to the bottom side
+		for (int row = 0; row < m_grid->rows_count(); row++)
+		{
+			m_grid->row(row).el_row->m_pos.y += captions_height;
+			for (int col = 0; col < m_grid->cols_count(); col++)
+			{
+				table_cell* cell = m_grid->cell(col, row);
+				if (cell->el)
+				{
+					cell->el->m_pos.y += captions_height;
+				}
+			}
+		}
+	}
+
 	calc_auto_margins(parent_width);
 
 	m_pos.move_to(x, y);
 	m_pos.x += content_margins_left();
 	m_pos.y += content_margins_top();
 	m_pos.width = table_width;
-	m_pos.height = table_height;
+	m_pos.height = table_height + captions_height;
 
 	return max_table_width;
 }
@@ -4734,6 +4782,11 @@ void litehtml::html_tag::draw_children_table(uint_ptr hdc, int x, int y, const p
 	position pos = m_pos;
 	pos.x += x;
 	pos.y += y;
+	for (auto& caption : m_grid->captions())
+	{
+		caption->draw(hdc, pos.x, pos.y, clip);
+		caption->draw_children(hdc, pos.x, pos.y, clip, flag, zindex);
+	}
 	for (int row = 0; row < m_grid->rows_count(); row++)
 	{
 		if (flag == draw_block)