diff --git a/hyperglass/render/html.py b/hyperglass/render/html.py
new file mode 100644
index 0000000..47fecb1
--- /dev/null
+++ b/hyperglass/render/html.py
@@ -0,0 +1,186 @@
+"""
+Renders Jinja2 & Sass templates for use by the front end application
+"""
+# Standard Library Imports
+from pathlib import Path
+
+# Third Party Imports
+import jinja2
+import yaml
+from logzero import logger
+from markdown2 import Markdown
+
+# Project Imports
+from hyperglass.configuration import devices
+from hyperglass.configuration import logzero_config # noqa: F401
+from hyperglass.configuration import params, networks
+from hyperglass.exceptions import HyperglassError
+
+# Module Directories
+working_directory = Path(__file__).resolve().parent
+hyperglass_root = working_directory.parent
+file_loader = jinja2.FileSystemLoader(str(working_directory))
+env = jinja2.Environment(
+ loader=file_loader, autoescape=True, extensions=["jinja2.ext.autoescape"]
+)
+
+default_details = {
+ "footer": """
+---
+template: footer
+---
+By using {{ branding.site_name }}, you agree to be bound by the following terms of \
+use: All queries executed on this page are logged for analysis and troubleshooting. \
+Users are prohibited from automating queries, or attempting to process queries in \
+bulk. This service is provided on a best effort basis, and {{ general.org_name }} \
+makes no availability or performance warranties or guarantees whatsoever.
+""",
+ "bgp_aspath": r"""
+---
+template: bgp_aspath
+title: Supported AS Path Patterns
+---
+{{ branding.site_name }} accepts the following `AS_PATH` regular expression patterns:
+
+| Expression | Match |
+| :------------------- | :-------------------------------------------- |
+| `_65000$` | Originated by 65000 |
+| `^65000_` | Received from 65000 |
+| `_65000_` | Via 65000 |
+| `_65000_65001_` | Via 65000 and 65001 |
+| `_65000(_.+_)65001$` | Anything from 65001 that passed through 65000 |
+""",
+ "bgp_community": """
+---
+template: bgp_community
+title: BGP Communities
+---
+{{ branding.site_name }} makes use of the following BGP communities:
+
+| Community | Description |
+| :-------- | :---------- |
+| `65000:1` | Example 1 |
+| `65000:2` | Example 2 |
+| `65000:3` | Example 3 |
+""",
+}
+
+default_info = {
+ "bgp_route": """
+---
+template: bgp_route
+---
+Performs BGP table lookup based on IPv4/IPv6 prefix.
+""",
+ "bgp_community": """
+---
+template: bgp_community
+---
+Performs BGP table lookup based on Extended or Large community value.
+
+""",
+ "bgp_aspath": """
+---
+template: bgp_aspath
+---
+Performs BGP table lookup based on `AS_PATH` regular expression.
+
+""",
+ "ping": """
+---
+template: ping
+---
+Sends 5 ICMP echo requests to the target.
+""",
+ "traceroute": """
+---
+template: traceroute
+---
+Performs UDP Based traceroute to the target.
For information about how to \
+interpret traceroute results, click here.
+""",
+}
+
+
+def generate_markdown(section, file_name):
+ """
+ Renders markdown as HTML. If file_name exists in appropriate
+ directory, it will be imported and used. If not, the default values
+ will be used. Also renders the Front Matter values within each
+ template.
+ """
+ if section == "info":
+ file = working_directory.joinpath(f"templates/info/{file_name}.md")
+ defaults = default_info
+ elif section == "details":
+ file = working_directory.joinpath(f"templates/info/details/{file_name}.md")
+ defaults = default_details
+ if file.exists():
+ with file.open(mode="r") as file_raw:
+ yaml_raw = file_raw.read()
+ else:
+ yaml_raw = defaults[file_name]
+ _, frontmatter, content = yaml_raw.split("---", 2)
+ html_classes = {"table": "ui compact table"}
+ markdown = Markdown(
+ extras={
+ "break-on-newline": True,
+ "code-friendly": True,
+ "tables": True,
+ "html-classes": html_classes,
+ }
+ )
+ frontmatter_rendered = (
+ jinja2.Environment(
+ loader=jinja2.BaseLoader,
+ autoescape=True,
+ extensions=["jinja2.ext.autoescape"],
+ )
+ .from_string(frontmatter)
+ .render(params)
+ )
+ if frontmatter_rendered:
+ frontmatter_loaded = yaml.safe_load(frontmatter_rendered)
+ elif not frontmatter_rendered:
+ frontmatter_loaded = {"frontmatter": None}
+ content_rendered = (
+ jinja2.Environment(
+ loader=jinja2.BaseLoader,
+ autoescape=True,
+ extensions=["jinja2.ext.autoescape"],
+ )
+ .from_string(content)
+ .render(params, info=frontmatter_loaded)
+ )
+ help_dict = dict(content=markdown.convert(content_rendered), **frontmatter_loaded)
+ if not help_dict:
+ raise HyperglassError(f"Error reading YAML frontmatter for {file_name}")
+ return help_dict
+
+
+def render_html(template_name, **kwargs):
+ """Renders Jinja2 HTML templates"""
+ details_name_list = ["footer", "bgp_aspath", "bgp_community"]
+ details_dict = {}
+ for details_name in details_name_list:
+ details_data = generate_markdown("details", details_name)
+ details_dict.update({details_name: details_data})
+ info_list = ["bgp_route", "bgp_aspath", "bgp_community", "ping", "traceroute"]
+ info_dict = {}
+ for info_name in info_list:
+ info_data = generate_markdown("info", info_name)
+ info_dict.update({info_name: info_data})
+ try:
+ template_file = f"templates/{template_name}.html.j2"
+ template = env.get_template(template_file)
+ return template.render(
+ params, info=info_dict, details=details_dict, networks=networks, **kwargs
+ )
+ except jinja2.TemplateNotFound as template_error:
+ logger.error(
+ f"Error rendering Jinja2 template {Path(template_file).resolve()}."
+ )
+ raise HyperglassError(template_error)