diff --git a/doc/classes/FlowContainer.xml b/doc/classes/FlowContainer.xml
new file mode 100644
index 00000000000..e98450f94af
--- /dev/null
+++ b/doc/classes/FlowContainer.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="FlowContainer" inherits="Container" version="3.5">
+	<brief_description>
+		Base class for flow containers.
+	</brief_description>
+	<description>
+		Arranges child [Control] nodes vertically or horizontally in a left-to-right or top-to-bottom flow.
+		A line is filled with [Control] nodes until no more fit on the same line, similar to text in an autowrapped label.
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+		<method name="get_line_count" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns the current line count.
+			</description>
+		</method>
+	</methods>
+	<constants>
+	</constants>
+</class>
diff --git a/doc/classes/HFlowContainer.xml b/doc/classes/HFlowContainer.xml
new file mode 100644
index 00000000000..031a85a7d81
--- /dev/null
+++ b/doc/classes/HFlowContainer.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="HFlowContainer" inherits="FlowContainer" version="3.5">
+	<brief_description>
+		Horizontal flow container.
+	</brief_description>
+	<description>
+		Horizontal version of [FlowContainer].
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+	</methods>
+	<constants>
+	</constants>
+	<theme_items>
+		<theme_item name="hseparation" data_type="constant" type="int" default="4">
+			The horizontal separation of children nodes.
+		</theme_item>
+		<theme_item name="vseparation" data_type="constant" type="int" default="4">
+			The vertical separation of children nodes.
+		</theme_item>
+	</theme_items>
+</class>
diff --git a/doc/classes/VFlowContainer.xml b/doc/classes/VFlowContainer.xml
new file mode 100644
index 00000000000..142968d763f
--- /dev/null
+++ b/doc/classes/VFlowContainer.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VFlowContainer" inherits="FlowContainer" version="3.5">
+	<brief_description>
+		Vertical flow container.
+	</brief_description>
+	<description>
+		Vertical version of [FlowContainer].
+	</description>
+	<tutorials>
+	</tutorials>
+	<methods>
+	</methods>
+	<constants>
+	</constants>
+	<theme_items>
+		<theme_item name="hseparation" data_type="constant" type="int" default="4">
+			The horizontal separation of children nodes.
+		</theme_item>
+		<theme_item name="vseparation" data_type="constant" type="int" default="4">
+			The vertical separation of children nodes.
+		</theme_item>
+	</theme_items>
+</class>
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 5f9dc3eeab7..e5581561de5 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1010,6 +1010,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 	theme->set_constant("margin_bottom", "MarginContainer", 0);
 	theme->set_constant("hseparation", "GridContainer", default_margin_size * EDSCALE);
 	theme->set_constant("vseparation", "GridContainer", default_margin_size * EDSCALE);
+	theme->set_constant("hseparation", "FlowContainer", default_margin_size * EDSCALE);
+	theme->set_constant("vseparation", "FlowContainer", default_margin_size * EDSCALE);
+	theme->set_constant("hseparation", "HFlowContainer", default_margin_size * EDSCALE);
+	theme->set_constant("vseparation", "HFlowContainer", default_margin_size * EDSCALE);
+	theme->set_constant("hseparation", "VFlowContainer", default_margin_size * EDSCALE);
+	theme->set_constant("vseparation", "VFlowContainer", default_margin_size * EDSCALE);
 
 	// WindowDialog
 	Ref<StyleBoxFlat> style_window = style_popup->duplicate();
diff --git a/editor/icons/icon_h_flow_container.svg b/editor/icons/icon_h_flow_container.svg
new file mode 100644
index 00000000000..dedf6e54f2a
--- /dev/null
+++ b/editor/icons/icon_h_flow_container.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.097 0-2 .903-2 2v10c0 1.097.903 2 2 2h10c1.097 0 2-.903 2-2v-10c0-1.097-.903-2-2-2zm0 2h10v10h-10zm2 1c-.554 0-1 .446-1 1s.446 1 1 1h2c.554 0 1-.446 1-1s-.446-1-1-1zm4.996 0c-.554 0-1 .446-1 1s.446 1 1 1h1.004c.554 0 1-.446 1-1s-.446-1-1-1zm-4.996 3c-.554 0-1 .446-1 1s.446 1 1 1 1-.446 1-1-.446-1-1-1zm3 0c-.554 0-1 .446-1 1s.446 1 1 1h3c.554 0 1-.446 1-1s-.446-1-1-1zm-3.004 3c-.554 0-1 .446-1 1s.446 1 1 1h4.004c.554 0 1-.446 1-1s-.446-1-1-1z" fill="#8eef97" fill-rule="nonzero"/></svg>
diff --git a/editor/icons/icon_v_flow_container.svg b/editor/icons/icon_v_flow_container.svg
new file mode 100644
index 00000000000..ccb0bea883b
--- /dev/null
+++ b/editor/icons/icon_v_flow_container.svg
@@ -0,0 +1 @@
+<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.097 0-2 .903-2 2v10c0 1.097.903 2 2 2h10c1.097 0 2-.903 2-2v-10c0-1.097-.903-2-2-2zm0 2h10v10h-10zm7.998.998c-.554 0-1 .446-1 1v4.004c0 .554.446 1 1 1s1-.446 1-1v-4.004c0-.554-.446-1-1-1zm-6 .004c-.554 0-1 .446-1 1v2c0 .554.446 1 1 1s1-.446 1-1v-2c0-.554-.446-1-1-1zm3 0c-.554 0-1 .446-1 1s.446 1 1 1 1-.446 1-1-.446-1-1-1zm0 3c-.554 0-1 .446-1 1v3c0 .554.446 1 1 1s1-.446 1-1v-3c0-.554-.446-1-1-1zm-3 1.996c-.554 0-1 .446-1 1v1.004c0 .554.446 1 1 1s1-.446 1-1v-1.004c0-.554-.446-1-1-1z" fill="#8eef97" fill-rule="nonzero"/></svg>
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
new file mode 100644
index 00000000000..8bdd58a2a04
--- /dev/null
+++ b/scene/gui/flow_container.cpp
@@ -0,0 +1,245 @@
+/*************************************************************************/
+/*  flow_container.cpp                                                   */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "scene/gui/container.h"
+
+#include "flow_container.h"
+
+struct _LineData {
+	int child_count = 0;
+	int min_line_height = 0;
+	int min_line_length = 0;
+	int stretch_avail = 0;
+	float stretch_ratio_total = 0;
+};
+
+void FlowContainer::_resort() {
+	int separation_horizontal = get_constant("hseparation");
+	int separation_vertical = get_constant("vseparation");
+
+	Map<Control *, Size2i> children_minsize_cache;
+
+	Vector<_LineData> lines_data;
+
+	Vector2i ofs;
+	int line_height = 0;
+	int line_length = 0;
+	float line_stretch_ratio_total = 0;
+	int current_container_size = vertical ? get_rect().size.y : get_rect().size.x;
+	int children_in_current_line = 0;
+
+	// First pass for line wrapping and minimum size calculation.
+	for (int i = 0; i < get_child_count(); i++) {
+		Control *child = Object::cast_to<Control>(get_child(i));
+		if (!child || !child->is_visible_in_tree()) {
+			continue;
+		}
+		if (child->is_set_as_toplevel()) {
+			continue;
+		}
+
+		Size2i child_msc = child->get_combined_minimum_size();
+
+		if (vertical) { /* VERTICAL */
+			if (children_in_current_line > 0) {
+				ofs.y += separation_vertical;
+			}
+			if (ofs.y + child_msc.y > current_container_size) {
+				line_length = ofs.y - separation_vertical;
+				lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+				// Move in new column (vertical line).
+				ofs.x += line_height + separation_horizontal;
+				ofs.y = 0;
+				line_height = 0;
+				line_stretch_ratio_total = 0;
+				children_in_current_line = 0;
+			}
+
+			line_height = MAX(line_height, child_msc.x);
+			if (child->get_v_size_flags() & SIZE_EXPAND) {
+				line_stretch_ratio_total += child->get_stretch_ratio();
+			}
+			ofs.y += child_msc.y;
+
+		} else { /* HORIZONTAL */
+			if (children_in_current_line > 0) {
+				ofs.x += separation_horizontal;
+			}
+			if (ofs.x + child_msc.x > current_container_size) {
+				line_length = ofs.x - separation_horizontal;
+				lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+				// Move in new line.
+				ofs.y += line_height + separation_vertical;
+				ofs.x = 0;
+				line_height = 0;
+				line_stretch_ratio_total = 0;
+				children_in_current_line = 0;
+			}
+
+			line_height = MAX(line_height, child_msc.y);
+			if (child->get_h_size_flags() & SIZE_EXPAND) {
+				line_stretch_ratio_total += child->get_stretch_ratio();
+			}
+			ofs.x += child_msc.x;
+		}
+
+		children_minsize_cache[child] = child_msc;
+		children_in_current_line++;
+	}
+	line_length = vertical ? (ofs.y) : (ofs.x);
+	lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+	// Second pass for in-line expansion and alignment.
+
+	int current_line_idx = 0;
+	int child_idx_in_line = 0;
+
+	ofs.x = 0;
+	ofs.y = 0;
+
+	for (int i = 0; i < get_child_count(); i++) {
+		Control *child = Object::cast_to<Control>(get_child(i));
+		if (!child || !child->is_visible_in_tree()) {
+			continue;
+		}
+		if (child->is_set_as_toplevel()) {
+			continue;
+		}
+		Size2i child_size = children_minsize_cache[child];
+
+		_LineData line_data = lines_data[current_line_idx];
+		if (child_idx_in_line >= lines_data[current_line_idx].child_count) {
+			current_line_idx++;
+			child_idx_in_line = 0;
+			if (vertical) {
+				ofs.x += line_data.min_line_height + separation_horizontal;
+				ofs.y = 0;
+			} else {
+				ofs.x = 0;
+				ofs.y += line_data.min_line_height + separation_vertical;
+			}
+			line_data = lines_data[current_line_idx];
+		}
+
+		if (vertical) { /* VERTICAL */
+			if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+				child_size.width = line_data.min_line_height;
+			}
+
+			if (child->get_v_size_flags() & SIZE_EXPAND) {
+				int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+				child_size.height += stretch;
+			}
+
+		} else { /* HORIZONTAL */
+			if (child->get_v_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+				child_size.height = line_data.min_line_height;
+			}
+
+			if (child->get_h_size_flags() & SIZE_EXPAND) {
+				int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+				child_size.width += stretch;
+			}
+		}
+
+		Rect2 child_rect = Rect2(ofs, child_size);
+		fit_child_in_rect(child, child_rect);
+
+		if (vertical) { /* VERTICAL */
+			ofs.y += child_size.height + separation_vertical;
+		} else { /* HORIZONTAL */
+			ofs.x += child_size.width + separation_horizontal;
+		}
+
+		child_idx_in_line++;
+	}
+	cached_size = (vertical ? ofs.x : ofs.y) + line_height;
+	cached_line_count = lines_data.size();
+}
+
+Size2 FlowContainer::get_minimum_size() const {
+	Size2i minimum;
+
+	for (int i = 0; i < get_child_count(); i++) {
+		Control *c = Object::cast_to<Control>(get_child(i));
+		if (!c) {
+			continue;
+		}
+		if (c->is_set_as_toplevel()) {
+			continue;
+		}
+
+		if (!c->is_visible()) {
+			continue;
+		}
+
+		Size2i size = c->get_combined_minimum_size();
+
+		if (vertical) { /* VERTICAL */
+			minimum.height = MAX(minimum.height, size.height);
+			minimum.width = cached_size;
+
+		} else { /* HORIZONTAL */
+			minimum.width = MAX(minimum.width, size.width);
+			minimum.height = cached_size;
+		}
+	}
+
+	return minimum;
+}
+
+void FlowContainer::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_SORT_CHILDREN: {
+			_resort();
+			minimum_size_changed();
+		} break;
+		case NOTIFICATION_THEME_CHANGED: {
+			minimum_size_changed();
+		} break;
+		case NOTIFICATION_TRANSLATION_CHANGED: {
+			queue_sort();
+		} break;
+	}
+}
+
+int FlowContainer::get_line_count() const {
+	return cached_line_count;
+}
+
+FlowContainer::FlowContainer(bool p_vertical) {
+	vertical = p_vertical;
+}
+
+void FlowContainer::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count);
+}
diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h
new file mode 100644
index 00000000000..1a40d0f7f18
--- /dev/null
+++ b/scene/gui/flow_container.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/*  flow_container.h                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef FLOW_CONTAINER_H
+#define FLOW_CONTAINER_H
+
+#include "scene/gui/container.h"
+
+class FlowContainer : public Container {
+	GDCLASS(FlowContainer, Container);
+
+private:
+	int cached_size;
+	int cached_line_count;
+
+	bool vertical;
+
+	void _resort();
+
+protected:
+	void _notification(int p_what);
+
+	static void _bind_methods();
+
+public:
+	int get_line_count() const;
+
+	virtual Size2 get_minimum_size() const;
+
+	FlowContainer(bool p_vertical = false);
+};
+
+class HFlowContainer : public FlowContainer {
+	GDCLASS(HFlowContainer, FlowContainer);
+
+public:
+	HFlowContainer() :
+			FlowContainer(false) {}
+};
+
+class VFlowContainer : public FlowContainer {
+	GDCLASS(VFlowContainer, FlowContainer);
+
+public:
+	VFlowContainer() :
+			FlowContainer(true) {}
+};
+
+#endif // FLOW_CONTAINER_H
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 45311c326e0..19729ed64cf 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -91,6 +91,7 @@
 #include "scene/gui/control.h"
 #include "scene/gui/dialogs.h"
 #include "scene/gui/file_dialog.h"
+#include "scene/gui/flow_container.h"
 #include "scene/gui/graph_edit.h"
 #include "scene/gui/graph_node.h"
 #include "scene/gui/grid_container.h"
@@ -336,6 +337,9 @@ void register_scene_types() {
 	ClassDB::register_class<CenterContainer>();
 	ClassDB::register_class<ScrollContainer>();
 	ClassDB::register_class<PanelContainer>();
+	ClassDB::register_virtual_class<FlowContainer>();
+	ClassDB::register_class<HFlowContainer>();
+	ClassDB::register_class<VFlowContainer>();
 
 	OS::get_singleton()->yield(); //may take time to init
 
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 0019d0fcc67..f2c46abca0a 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -890,6 +890,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 	theme->set_constant("separation", "VSplitContainer", 12 * scale);
 	theme->set_constant("autohide", "HSplitContainer", 1 * scale);
 	theme->set_constant("autohide", "VSplitContainer", 1 * scale);
+	theme->set_constant("hseparation", "HFlowContainer", 4 * scale);
+	theme->set_constant("vseparation", "HFlowContainer", 4 * scale);
+	theme->set_constant("hseparation", "VFlowContainer", 4 * scale);
+	theme->set_constant("vseparation", "VFlowContainer", 4 * scale);
 
 	Ref<StyleBoxTexture> sb_pc = make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 7, 7, 7, 7);
 	theme->set_stylebox("panel", "PanelContainer", sb_pc);