Skip to content

Templates

This page shows how to use templates with Vix Core.

Use it when you want to render HTML pages from route handlers using a template directory and a rendering context.

Public header

cpp
#include <vix.hpp>

You can also include the app and template headers directly:

cpp
#include <vix/app/App.hpp>
#include <vix/template/Context.hpp>
#include <vix/view/TemplateView.hpp>

What templates provide

Templates let a Vix application generate HTML responses from files.

Use templates for:

  • HTML pages
  • dashboards
  • server-rendered views
  • admin panels
  • forms
  • emails or HTML fragments
  • dynamic pages with variables

Templates are configured with app.templates(...).

cpp
app.templates("views");

Then handlers can call res.render(...).

cpp
res.render("index.html", ctx);

Basic template setup

cpp
#include <vix.hpp>

int main()
{
  vix::App app;

  app.templates("views");

  app.get("/", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    vix::tmpl::Context ctx;
    ctx.set("title", "Home");

    res.render("index.html", ctx);
  });

  app.run(8080);

  return 0;
}

Expected directory:

text
views/
  index.html

Public alias

Vix Core exposes the template namespace as:

cpp
vix::tmpl

So you can write:

cpp
vix::tmpl::Context ctx;

instead of referencing the internal template namespace directly.

Configure the template directory

Use templates(...) on the app.

cpp
app.templates("views");

This configures the root directory used to load template files.

Example:

text
views/
  index.html
  about.html
  users/
    show.html

Then you can render:

cpp
res.render("index.html", ctx);
res.render("about.html", ctx);
res.render("users/show.html", ctx);

Render a template

Use res.render(name, context).

cpp
app.get("/", [](vix::Request &req, vix::Response &res)
{
  (void)req;

  vix::tmpl::Context ctx;
  ctx.set("title", "Welcome");

  res.render("index.html", ctx);
});

name is the template path relative to the template directory.

text
views/index.html

is rendered with:

cpp
res.render("index.html", ctx);

Template context

The context stores values passed to the template.

cpp
vix::tmpl::Context ctx;

ctx.set("title", "Home");
ctx.set("message", "Hello from Vix");

Then render:

cpp
res.render("index.html", ctx);

Render with route parameters

cpp
#include <vix.hpp>

int main()
{
  vix::App app;

  app.templates("views");

  app.get("/users/{id}", [](vix::Request &req, vix::Response &res)
  {
    vix::tmpl::Context ctx;
    ctx.set("title", "User profile");
    ctx.set("user_id", req.param("id"));

    res.render("users/show.html", ctx);
  });

  app.run(8080);

  return 0;
}

Request:

text
GET /users/42

Template path:

text
views/users/show.html

Render with query parameters

cpp
app.get("/search", [](vix::Request &req, vix::Response &res)
{
  vix::tmpl::Context ctx;
  ctx.set("title", "Search");
  ctx.set("q", req.query_value("q", ""));

  res.render("search.html", ctx);
});

Request:

text
GET /search?q=vix

Render from middleware-provided state

Middleware can store data in the request state.

cpp
struct CurrentUser
{
  std::string id;
  std::string name;
};

Store the user:

cpp
app.use([](vix::Request &req, vix::Response &res, vix::App::Next next)
{
  (void)res;

  req.state().set(CurrentUser{"42", "Ada"});

  next();
});

Use it in a handler:

cpp
app.get("/me", [](vix::Request &req, vix::Response &res)
{
  const auto &user = req.state().get<CurrentUser>();

  vix::tmpl::Context ctx;
  ctx.set("title", "My profile");
  ctx.set("user_id", user.id);
  ctx.set("user_name", user.name);

  res.render("me.html", ctx);
});

Check if templates are configured

Use has_views().

cpp
if (app.has_views())
{
  vix::print("templates are configured");
}

This is useful for advanced setup code or plugins.

Access the template view

Use views() to access the template rendering facade.

cpp
auto &view = app.views();

If templates(...) was not called, views() throws.

cpp
if (app.has_views())
{
  auto &view = app.views();
  (void)view;
}

Most applications do not need to access views() directly.

They use:

cpp
res.render("index.html", ctx);

Templates and Response

res.render(...) is part of the public response helper.

cpp
res.render("index.html", ctx);

It uses the template view configured by app.templates(...).

If templates are not configured, rendering fails.

Recommended:

cpp
app.templates("views");

app.get("/", [](vix::Request &req, vix::Response &res)
{
  (void)req;

  vix::tmpl::Context ctx;
  ctx.set("title", "Home");

  res.render("index.html", ctx);
});

Templates and static files

Templates and static files are different.

Use templates for dynamic HTML:

cpp
app.templates("views");

app.get("/", [](vix::Request &req, vix::Response &res)
{
  (void)req;

  vix::tmpl::Context ctx;
  ctx.set("title", "Home");

  res.render("index.html", ctx);
});

Use static files for assets:

cpp
app.static_dir("public/assets", "/assets");

Common structure:

text
views/
  index.html

public/
  assets/
    app.css
    app.js
    logo.svg

Template with assets

Example app:

cpp
#include <vix.hpp>

int main()
{
  vix::App app;

  app.templates("views");
  app.static_dir("public/assets", "/assets");

  app.get("/", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    vix::tmpl::Context ctx;
    ctx.set("title", "Home");

    res.render("index.html", ctx);
  });

  app.run(8080);

  return 0;
}

Template can reference assets like:

html
<link rel="stylesheet" href="/assets/app.css">
<script src="/assets/app.js"></script>

Template page example

Example template file:

html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>{{ title }}</title>
</head>
<body>
  <h1>{{ title }}</h1>
  <p>{{ message }}</p>
</body>
</html>

Example handler:

cpp
app.get("/", [](vix::Request &req, vix::Response &res)
{
  (void)req;

  vix::tmpl::Context ctx;
  ctx.set("title", "Welcome");
  ctx.set("message", "Hello from Vix");

  res.render("index.html", ctx);
});

Multiple pages

cpp
#include <vix.hpp>

int main()
{
  vix::App app;

  app.templates("views");

  app.get("/", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    vix::tmpl::Context ctx;
    ctx.set("title", "Home");

    res.render("index.html", ctx);
  });

  app.get("/about", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    vix::tmpl::Context ctx;
    ctx.set("title", "About");

    res.render("about.html", ctx);
  });

  app.run(8080);

  return 0;
}

Directory:

text
views/
  index.html
  about.html

Templates in route groups

Templates work normally inside route groups.

cpp
app.group("/admin", [](auto &admin)
{
  admin.get("/dashboard", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    vix::tmpl::Context ctx;
    ctx.set("title", "Dashboard");

    res.render("admin/dashboard.html", ctx);
  });
});

Route:

text
GET /admin/dashboard

Template:

text
views/admin/dashboard.html

Templates with middleware

Middleware can prepare data for template handlers.

cpp
struct PageInfo
{
  std::string app_name;
};

Middleware:

cpp
app.use([](vix::Request &req, vix::Response &res, vix::App::Next next)
{
  (void)res;

  req.state().set(PageInfo{"Vix.cpp"});

  next();
});

Handler:

cpp
app.get("/", [](vix::Request &req, vix::Response &res)
{
  const auto &page = req.state().get<PageInfo>();

  vix::tmpl::Context ctx;
  ctx.set("title", "Home");
  ctx.set("app_name", page.app_name);

  res.render("index.html", ctx);
});

Templates and errors

If a template cannot be rendered, the route handler may throw.

For user-facing apps, you can catch errors and return a response.

cpp
app.get("/", [](vix::Request &req, vix::Response &res)
{
  (void)req;

  try
  {
    vix::tmpl::Context ctx;
    ctx.set("title", "Home");

    res.render("index.html", ctx);
  }
  catch (const std::exception &e)
  {
    res.status(500).text("Template error");
  }
});

Templates and JSON APIs

You can mix template pages and JSON APIs.

cpp
#include <vix.hpp>

int main()
{
  vix::App app;

  app.templates("views");

  app.get("/", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    vix::tmpl::Context ctx;
    ctx.set("title", "Home");

    res.render("index.html", ctx);
  });

  app.get("/api/status", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    res.json({
      {"status", "ok"}
    });
  });

  app.run(8080);

  return 0;
}

Templates and redirects

A template handler can redirect instead of rendering.

cpp
app.get("/dashboard", [](vix::Request &req, vix::Response &res)
{
  const bool logged_in = req.has_header("Authorization");

  if (!logged_in)
  {
    res.redirect("/login");
    return;
  }

  vix::tmpl::Context ctx;
  ctx.set("title", "Dashboard");

  res.render("dashboard.html", ctx);
});

Templates and status codes

Set the status before rendering.

cpp
app.get("/missing-page", [](vix::Request &req, vix::Response &res)
{
  (void)req;

  vix::tmpl::Context ctx;
  ctx.set("title", "Not found");

  res.status(404).render("404.html", ctx);
});

Template lifecycle

The template lifecycle is:

text
App::templates
  -> create FileSystemLoader
  -> create template Engine
  -> create TemplateView
  -> route handler builds Context
  -> ResponseWrapper::render
  -> TemplateView renders response
  -> Session writes HTTP response
text
project/
  src/
    main.cpp
  views/
    index.html
    about.html
    errors/
      404.html
  public/
    assets/
      app.css
      app.js
      logo.svg

Example setup:

cpp
app.templates("views");
app.static_dir("public/assets", "/assets");

Complete example

cpp
#include <vix.hpp>

#include <string>

struct CurrentUser
{
  std::string id;
  std::string name;
};

int main()
{
  vix::App app;

  app.templates("views");
  app.static_dir("public/assets", "/assets");

  app.use([](vix::Request &req, vix::Response &res, vix::App::Next next)
  {
    (void)res;

    req.state().set(CurrentUser{"42", "Ada"});

    next();
  });

  app.get("/", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    vix::tmpl::Context ctx;
    ctx.set("title", "Home");
    ctx.set("message", "Hello from Vix");

    res.render("index.html", ctx);
  });

  app.get("/me", [](vix::Request &req, vix::Response &res)
  {
    const auto &user = req.state().get<CurrentUser>();

    vix::tmpl::Context ctx;
    ctx.set("title", "Profile");
    ctx.set("user_id", user.id);
    ctx.set("user_name", user.name);

    res.render("me.html", ctx);
  });

  app.get("/api/status", [](vix::Request &req, vix::Response &res)
  {
    (void)req;

    res.json({
      {"status", "ok"}
    });
  });

  app.run(8080);

  return 0;
}

Directory:

text
views/
  index.html
  me.html

public/
  assets/
    app.css

API summary

APIPurpose
app.templates(directory)Configure the template root directory.
app.has_views()Return whether templates are configured.
app.views()Access the template view facade.
vix::tmpl::ContextStore values passed to templates.
ctx.set(name, value)Store a template variable.
res.render(name, context)Render a template response.
app.static_dir(root, mount)Serve static assets used by templates.

Best practices

Configure templates before routes that render views.

cpp
app.templates("views");

Keep templates in a dedicated directory.

text
views/

Keep assets in a public directory.

text
public/assets/

Mount assets under /assets.

cpp
app.static_dir("public/assets", "/assets");

Build the context explicitly in the handler.

cpp
vix::tmpl::Context ctx;
ctx.set("title", "Home");

Return immediately after redirects or errors.

cpp
if (!logged_in)
{
  res.redirect("/login");
  return;
}

Use JSON routes for APIs and templates for pages.

cpp
app.get("/api/status", api_handler);
app.get("/", page_handler);

Next steps

Read the next pages:

Released under the MIT License.