add in-memory includes via include_template
diff --git a/README.md b/README.md
index 9a59048..628c075 100644
--- a/README.md
+++ b/README.md
@@ -157,9 +157,14 @@
 
 #### Includes
 
-This includes other template files, relative from the current file location.
+You can either include other template files or already parsed templates.
 ```
-{% include "footer.html" %}
+// Other template files are included relative from the current file location
+render({% include "footer.html" %}, data);
+
+// To include in-memory templates, add them to the environment first
+env.include_template("footer", temp);
+render({% include "footer" %}, data);
 ```
 
 ### Functions
diff --git a/src/inja.hpp b/src/inja.hpp
index b1d4c65..56f051b 100644
--- a/src/inja.hpp
+++ b/src/inja.hpp
@@ -614,6 +614,10 @@
 public:
 	ElementNotation element_notation = ElementNotation::Pointer;
 
+	std::map<Parsed::CallbackSignature, Regex, std::greater<Parsed::CallbackSignature>> regex_map_callbacks;
+
+	std::map<const std::string, Template> included_templates;
+
 	/*!
 	@brief create a corresponding regex for a function name with a number of arguments seperated by ,
 	*/
@@ -704,8 +708,6 @@
 		{Parsed::Function::ReadJson, Regex{"\\s*([^\\(\\)]*\\S)\\s*"}}
 	};
 
-	std::map<Parsed::CallbackSignature, Regex, std::greater<Parsed::CallbackSignature>> regex_map_callbacks;
-
 	Parser() { }
 
 	Parsed::ElementExpression parse_expression(const std::string& input) {
@@ -859,11 +861,16 @@
 							break;
 						}
 						case Parsed::Statement::Include: {
-							std::string included_filename = path + match_statement.str(1);
-							Template included_template = parse_template(included_filename);
-							for (auto& element: included_template.parsed_template().children) {
-								result.emplace_back(element);
+							std::string template_name = match_statement.str(1);
+							Template included_template;
+							if (included_templates.find( template_name ) != included_templates.end()) {
+								included_template = included_templates[template_name];
+							} else {
+								included_template = parse_template(path + template_name);
 							}
+
+							auto children = included_template.parsed_template().children;
+							result.insert(result.end(), children.begin(), children.end());
 							break;
 						}
 					}
@@ -1022,6 +1029,10 @@
 		renderer.map_callbacks[signature] = callback;
 	}
 
+	void include_template(std::string name, const Template& temp) {
+		parser.included_templates[name] = temp;
+	}
+
 	template<typename T = json>
 	T get_argument(const Parsed::Arguments& args, int index, const json& data) {
 		return renderer.eval_expression<T>(args[index], data);
diff --git a/test/src/unit-renderer.cpp b/test/src/unit-renderer.cpp
index 4acad8c..d2f8a88 100644
--- a/test/src/unit-renderer.cpp
+++ b/test/src/unit-renderer.cpp
@@ -288,19 +288,30 @@
 }
 
 TEST_CASE("templates") {
-	inja::Environment env = inja::Environment();
-	inja::Template temp = env.parse("{% if is_happy %}{{ name }}{% else %}{{ city }}{% endif %}");
-
 	json data;
 	data["name"] = "Peter";
 	data["city"] = "Brunswick";
 	data["is_happy"] = true;
 
-	CHECK( env.render_template(temp, data) == "Peter" );
+	SECTION("reuse") {
+		inja::Environment env = inja::Environment();
+		inja::Template temp = env.parse("{% if is_happy %}{{ name }}{% else %}{{ city }}{% endif %}");
 
-	data["is_happy"] = false;
+		CHECK( env.render_template(temp, data) == "Peter" );
 
-	CHECK( env.render_template(temp, data) == "Brunswick" );
+		data["is_happy"] = false;
+
+		CHECK( env.render_template(temp, data) == "Brunswick" );
+	}
+
+	SECTION("include") {
+		inja::Environment env = inja::Environment();
+		inja::Template t1 = env.parse("Hello {{ name }}");
+		env.include_template("greeting", t1);
+
+		inja::Template t2 = env.parse("{% include \"greeting\" %}!");
+		CHECK( env.render_template(t2, data) == "Hello Peter!" );
+	}
 }
 
 TEST_CASE("other-syntax") {