# Getting started DFTE renders a named root template through a `TemplateContext`. Register templates and placeholder values once, then ask the renderer for fixed-size chunks until completion. ```cpp #include #include static const char ROOT[] PROGMEM = R"DFTE(

%TITLE%

Uptime: %UPTIME%

)DFTE"; PlaceholderRegistry registry; TemplateContext context; void setup() { Serial.begin(115200); registry.registerProgmemData(PSTR("%TITLE%"), PSTR("DFTE Quickstart")); registry.registerRamData(PSTR("%UPTIME%"), []() -> const char* { static char value[16]; snprintf(value, sizeof(value), "%lus", millis() / 1000); return value; }); registry.registerProgmemTemplate(PSTR("%ROOT%"), ROOT); context.setRegistry(®istry); TemplateRenderer::initializeContext(context, PSTR("%ROOT%")); } void loop() { if (TemplateRenderer::hasError(context)) { // Report the invalid root or placeholder once in production, then stop this response. return; } if (TemplateRenderer::isComplete(context)) return; uint8_t chunk[128]; const size_t written = TemplateRenderer::renderNextChunk(context, chunk, sizeof(chunk)); if (written > 0) Serial.write(chunk, written); } ``` `registerProgmemData()` is for static flash data. `registerRamData()` takes a getter for a value that can change as chunks are rendered. `registerProgmemTemplate()` lets a placeholder expand to another template. For a buildable project, start with [HelloPlaceholder](../examples/HelloPlaceholder/). Next: [template language](TEMPLATE_LANGUAGE.md). Back to [documentation](README.md) ยท [project overview](../README.md).