partial

package module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 19 Imported by: 0

README

go-partial

go-partial

Documentation · Part of go-webthings.

This package provides a request-aware rendering layer for Go templates. It lets applications render full pages or targeted partials from the same registered template tree, with wrapper/content composition, connector headers, OOB output, caching, and typed template-friendly data flow. go-partial supersedes go-htmx. The rendering core stays small; connectors and optional extensions add the browser integration your application needs.

Features

  • Partial Templates: Define and render partial templates with typed dot data and functions.
  • Native Template Composition: Use {{ template "row.gohtml" . }} with SetDot so reusable sections can render inside a page or as HTMX targets.
  • Wrapper Content: Use wrapper partials with content children while keeping page data explicit.
  • Template Caching: Enable caching of parsed templates for improved performance.
  • Out-of-Band Rendering: Support for rendering out-of-band (OOB) partials.
  • File System Support: Use custom fs.FS implementations for template file access.
  • Concurrent Rendering: Configure partial trees up front, then reuse them safely across concurrent requests.

Installation

To install the package, run:

go get github.com/donseba/go-partial

Template functions

Template-facing helpers and accessors are documented in TEMPLATE_FUNCTIONS.md.

Package Shape

The root package is intentionally small: rendering lifecycle, partial trees, runtime context, connectors, Root partial configuration, and core template helpers. Core does not import optional packages.

Optional packages are split by stability:

  • ext/... contains extension packages that are useful but not required by core, such as ext/errors and ext/debug.
  • exp/... contains experimental opt-in features, such as localization, CSRF, flash messages, selection, actions, pageflow, interactions, metrics, OpenTelemetry, slots, target resolvers, template helpers, and SSE.

Applications choose the pieces they want with SetFunc(...), Use(...), or package-specific setup helpers. A render stage follows the same lifecycle everywhere: Prepare can add request-scoped context, Render wraps or replaces template rendering, and Finalize observes or transforms the result.

Render stages may set generic response metadata through ctx.Response. partial.Write applies that status and those headers after rendering. Templates do not receive helpers for setting headers or status; templates produce HTML.

Rendering

For partials, prefer the package-level rendering functions:

html, err := partial.Render(ctx, content)
html, err = partial.RenderWithRequest(ctx, r, content)
err = partial.Write(ctx, w, r, content)

partial.Write owns HTTP response behavior: configured headers, connector response headers, render-stage response metadata, error fragments, and out-of-band regions. Partial itself does not render or write responses; pass it to the package functions.

Metrics Output

exp/metrics records render lifecycle data through a small Sink interface. Use your own sink for storage, or write JSON lines to any io.Writer:

sink := metrics.Fanout(
    inMemoryStore,
    metrics.NewWriterSink(os.Stdout),
)

root.Use(metrics.Stage(sink, metrics.WithTag("chain", "web")))

The writer sink is intentionally plain: it works with stdout, files, buffers, pipes, or app-owned adapters that forward records to SSE, queues, or databases.

Metrics records distinguish request identity from flow identity. WithRequestID labels the current HTTP request. WithTraceID can group related browser work such as a page request and later async or SSE requests. WithParentRequestID can link a spawned request back to the request that caused it.

Diagnostic Events

Core and extensions can emit diagnostic events without owning logging, tracing, or queues. Attach shared consumers to the reusable root partial:

events := partial.NewAsyncEvents(
    partial.EventsConfig{Buffer: 256, DropPolicy: partial.DropNewest},
    logger.Sink(slog.Default(), logger.WithMinLevel(partial.EventWarn)),
    mySink,
)

root := partial.New("templates/shell.gohtml").
    SetEvents(events)

ext/logger adapts events to log/slog. App-owned sinks can forward the same events to DynamoDB, Kafka, OpenTelemetry, SSE debug pages, files, or any other collector. See OBSERVABILITY.md for concrete sink examples.

For request-owned collectors, attach a sink with partial.WithEventSink(r.Context(), sink) and close request-owned async dispatchers in middleware.

Example Applications

A documentation-style site built with go-partial is available in examples/docs.

go run ./examples/docs

Open http://localhost:8091.

An htmx-backed feature showcase with real template files is available in examples/showcase.

go run ./examples/showcase

Open http://localhost:8090 to see pages for typed rows, selection partials, actions, flash messages, out-of-band rendering, context helpers, localization, HTMX response headers, server-sent events, live metrics, infinite scroll with cursor-style X-Action values, and the optional error page.

Integrations

Several integrations are available, detailed information can be found in the INTEGRATIONS.md file.

  • htmx
  • Turbo
  • Unpoly
  • Partial, for framework-neutral fetch clients and tests
  • SSE writer, for streaming rendered HTML patches

Error Output

partial.Write can render an HTML error page when template parsing or execution fails, but only when an error stage is registered. Register ext/errors to choose the failure markup. In detailed mode, the page includes the partial ID, template list, request URL, template location, and original error so development and failed htmx requests still return useful output.

Normal requests receive a 500 full HTML page. HTMX/partial requests receive a swappable error fragment with status 200, because HTMX does not swap 500 responses by default.

Register the error stage on the reusable root partial:

root := partial.New("templates/shell.gohtml").
    Use(exterrors.Stage(exterrors.WithMode(exterrors.ModeDetailed)))

Or on one partial:

content.Use(exterrors.Stage())

partial.RenderWithRequest still returns the render error directly. partial.Write asks the render stage chain for a failure response; without ext/errors, it returns the original render error.

Localization

Templates receive a request localizer through the localizer and locale helpers from exp/localization. The interface only requires GetLocale(). Translation behavior should come from user-provided template functions registered with Partial.SetFunc:

root.SetFunc(localization.FuncMap(), translator.FuncMap())
root.Use(localization.Stage())
ctx := localization.WithLocalizer(r.Context(), localizer)
_ = partial.Write(ctx, w, r, content)
<p>{{ tl localizer "Hello, World!" }}</p>
<p>{{ tn localizer "You have one message." "You have %d messages." 5 5 }}</p>
<button>{{ ctl localizer "button" "save" }}</button>

tl, tn, ctl, and ctn are not built into go-partial. For a fuller translation backend, github.com/donseba/go-translator fits this pattern well because it exposes a compatible FuncMap().

HTMX Response Helpers

The configured connector turns partial response instructions into protocol-specific response headers:

content := partial.NewID("notice", "notice.gohtml")

content.Response().
    Retarget("#notice").
    ReswapWith(connector.NewSwap().Style(connector.SwapOuterHTML).Transition(true)).
    TriggerWith(connector.NewTrigger().AddEventObject("notice", map[string]any{
        "message": "Saved",
    }))

You can also set the response instructions as data:

content.SetResponse(connector.Response{
    Retarget: "#notice",
    Trigger: connector.NewTrigger().AddEventObject("notice", map[string]any{
        "message": "Saved",
    }).String(),
})

With the HTMX connector, these become HX-Retarget, HX-Reswap, and HX-Trigger headers during partial.Write.

Debug Helper

The debug template helper renders a styled diagnostic box using an embedded template:

{{ debug runtime . }}

Register the debug helper and stage globally, per wrapper, or per partial:

root.SetFunc(debug.FuncMap())
root.Use(debug.Stage())

Server-Sent Events

SSE is a writer layer, not a connector. Use it after deciding which partials changed:

events := sse.NewWriter(w)

notice := partial.NewID("notice", "notice.gohtml").
    SetDot(Notice{Message: "Saved"})

_ = events.PatchPartial(r.Context(), r, "#notice", notice)
_ = events.Signal("saved", true)
events.Flush()

Flush follows middleware wrappers that expose Unwrap() http.ResponseWriter and honors their FlushError methods.

The writer declares constants for expected headers and event names, such as HeaderContentType, ContentTypeEventStream, EventPatch, EventSignal, and EventError.

Basic Usage

Here's a simple typed example. The template owns the contract, the handler supplies the model.

1. Create A Root Blueprint

The root partial holds shared render configuration and is cloned per request.

root := partial.NewID("shell", "templates/shell.html").
    SetConnector(connector.NewHTMX(nil)).
    SetFileSystem(os.DirFS("web")).
    UseTemplateCache(true).
    SetEvents(events).
    SetFunc(template.FuncMap{
        "money": formatMoney,
    })

2. Compose The Root Partial Per Request

A wrapper partial can render one configured content child through {{ content }}. Create the tree inside the handler when the content, wrapper, dot data, or registered children are request-specific. Clone the configured root blueprint per request; per-request wiring should stay on the clone.

3. Define Partials

Create Partial instances for the content and any other components.

type ContentPage struct {
    PageTitle string
    Message string
}

type ShellPage struct {
    AppName string
}

func handler(w http.ResponseWriter, r *http.Request) {
    // Create the main content partial
    content := partial.NewID("content", "templates/content.html").
        SetDot(ContentPage{
            PageTitle: "Home Page",
            Message:   "Welcome to our website!",
        })
    
    // Clone the shared shell and attach request-specific content.
    page := root.Clone().
        SetDot(ShellPage{AppName: "My Application"})
    page.SetContent(content)
    
    output, err := partial.RenderWithRequest(r.Context(), r, page)
    if err != nil {
        http.Error(w, "An error occurred while rendering the page.", http.StatusInternalServerError)
        return
    }
    w.Write([]byte(output))
}

Template Files

templates/shell.html

<!DOCTYPE html>
<html>
<head>
    <title>{{.AppName}}</title>
</head>
<body>
    {{ content }}
</body>
</html>

templates/content.html

<h1>{{ .PageTitle }}</h1>
<p>{{ .Message }}</p>

Note: When a wrapper partial wraps content, it renders the configured route partial by calling {{ content }}.

Accessing Data in Templates

Use SetDot for application data:

{{ .PageTitle }}
{{ .Message }}

For shared application values, put them on a typed model and declare them with go-doc @model:

{{/*
@model App github.com/example/app.AppInfo
*/}}

{{ App.AppName }}
wrapper.SetModel(AppInfo)

When SetDot is used, request-specific values are available through helpers instead of fields on dot:

{{ ctx.URL.Path }}
{{ ctx.URL.Path }}
{{ request.Method }}
{{ locale }}
{{ csrf.Key }}
{{ basePath }}

Typed Contracts

go-partial can register go-doc typed root declarations before parsing templates. The declaration owns the template name, while the controller supplies the matching Go value:

{{/*
@model Page github.com/example/app.DashboardPage
*/}}

<h1>{{ Page.Title }}</h1>
content := partial.NewID("content", "templates/dashboard.gohtml").
    SetModel(page)

SetModel appends values to the typed roots already inherited through the partial tree. Use it when a partial adds local models on top of parent models. SetContract(annotation, values...) does the same thing for custom annotation names.

When more than one root has the same Go type, bind by name:

{{/*
@interaction LikesPoll github.com/donseba/go-partial/exp/interactions.Interaction
@interaction LikeButton github.com/donseba/go-partial/exp/interactions.Interaction
*/}}

{{ poll runtime LikesPoll }}
{{ refresh runtime LikeButton }}
content.SetContract("interaction",
    interactions.NewPoll("/posts/42/likes").As("LikesPoll").Every(5*time.Second),
    interactions.NewRefresh("/posts/42/likes").As("LikeButton").Target("#likes"),
)

Interaction roots are normal go-doc contracts registered with SetContract("interaction", values...). Use As(name) when the endpoint does not naturally produce the contract name; otherwise the last endpoint segment is capitalized, so /stats becomes Stats.

Contract names cannot collide with go-partial helpers such as partial, locale, ctx, or url.

go-partial keeps interaction template helpers in github.com/donseba/go-partial/exp/interactions. They are opt-in:

import "github.com/donseba/go-partial/exp/interactions"

root.SetFunc(interactions.FuncMap())

Those functions include go-doc signature metadata for the overloads that normal Go function declarations cannot express. The repository .go-doc/config.json wires them into editor tooling for this repo so helper calls such as {{ async runtime "/stats" }} and {{ async runtime Stats }} can both be completed and validated. The runtime argument is a per-render value injected by go-partial; it exposes request context, the active connector, partial tree, and diagnostic stages to helpers without package globals.

Use the inline endpoint form when the endpoint is naturally local to the template, especially inside loops:

{{ range .Rows }}
    {{ async runtime "/async/row/:row" "row" .ID }}
{{ end }}

Use a named interaction when Go should own stable IDs, targets, intervals, events, placeholders, or reuse:

{{/*
@interaction LikesPoll github.com/donseba/go-partial/exp/interactions.Interaction
*/}}
{{ poll runtime LikesPoll }}
content.SetContract("interaction",
    interactions.NewPoll("/posts/42/likes").As("LikesPoll").Every(5*time.Second),
)

Template Composition

For repeated sections that should be understood by go-doc and also render as HTMX targets, prefer native templates plus SetDot. The parent owns the loop, and the nested template receives the row as normal dot data:

{{/*
@dot github.com/example/app.TablePage
*/}}
{{ range .Rows }}
    {{ template "row.html" . }}
    {{/* or: {{ template "/templates/row.html" . }} */}}
{{ end }}
<tr id="row-{{ .ID }}">
    <td>{{ .Name }}</td>
</tr>

Register the row partial on the parent so the parent parse can see the row template and so an HTMX request can still target a row by ID:

rowPartial := partial.NewID("row", "templates/row.html")
table.With(rowPartial)
target.WithResolver(table, func(ctx context.Context, r *http.Request, target string) (*partial.Partial, bool) {
    row := findRowForTarget(target)
    return partial.NewID(target, "templates/row.html").SetDot(row), true
})

For wrapper partials, render the route content through content:

<main>{{ content }}</main>

Avoid using partial as a general row-composition helper in new code. Native template calls keep the template idiomatic and give go-doc the strongest type information. Use partial when you intentionally want to render a template path through go-partial's render path:

{{ partial runtime "templates/notice.gohtml" .Notice }}

Keep registered partials for HTMX targets, OOB output, selection/action rendering, and places where the browser can request a stable partial ID.

Using Out-of-Band (OOB) Partials

Out-of-Band partials allow you to update parts of the page without reloading:

Defining an OOB Partial
type Footer struct {
    Text string
}

// Create the OOB partial
footer := partial.New("templates/footer.html").ID("footer")
footer.SetDot(Footer{
    Text: "This is the footer",
})

// Add the OOB partial
p.WithOOB(footer)
Using OOB Partials in Templates

In your templates, you can use the oobAttr function to conditionally render OOB attributes.

templates/footer.html

<div{{ oobAttr }} id="footer">{{ .Text }}</div>

Template Functions

You can add custom functions to be used within your templates:

import "strings"

// Define custom functions
funcs := template.FuncMap{
    "upper": strings.ToUpper,
}

// Merge the functions into this partial tree
p.SetFunc(funcs)

SetFunc registers helpers in the current scope. Function names inherited from the Root partial or parent partial remain available, and protected go-partial helper names cannot be overwritten.

go-partial reserves only the helpers it injects for rendering and request state: runtime, partial, content, ctx, request, url, locale, csrf, OOB helpers, and connector helpers such as targetIs, selectionIs, and actionIs. Generic helpers such as dict, string helpers, and date helpers are ordinary template functions and may be replaced.

Optional helper providers live under exp/:

import (
    "github.com/donseba/go-partial/exp/flash"
    "github.com/donseba/go-partial/exp/interactions"
    "github.com/donseba/go-partial/exp/templatehelpers"
)

root.SetFunc(
    flash.FuncMap(),
    interactions.FuncMap(),
    templatehelpers.StringFuncMap(),
    templatehelpers.CollectionFuncMap(),
)

For go-doc, point provider discovery at the same packages:

{
  "providers": [
    "github.com/donseba/go-partial/exp/flash",
    "github.com/donseba/go-partial/exp/interactions",
    "github.com/donseba/go-partial/exp/templatehelpers"
  ]
}
Usage in Template:
{{ upper .Message }}
Using a Custom File System

If your templates are stored in a custom file system, set it with SetFileSystem:

import (
    "embed"
)

//go:embed templates/*
var templatesFS embed.FS

p.SetFileSystem(templatesFS)

If you do not use a custom file system, the package will use the default file system and look for templates relative to the current working directory.

Rendering Tables and Dynamic Content

For tables and repeated fragments, prefer native Go templates plus SetDot. The parent receives a typed page model, ranges over rows, and calls the row template with {{ template "row.html" . }}. That keeps the template readable for go-doc while go-partial still knows the row partial ID for HTMX target requests.

Example: Rendering a Table with Dynamic Rows

templates/table.html

<table>
    {{ range .Rows }}
        {{ template "row.html" . }}
    {{ end }}
</table>

templates/row.html

<tr id="row-{{ .ID }}">
    <td>{{ .Name }}</td>
</tr>

Go Code:

type Row struct {
    ID   int
    Name string
}

type TablePage struct {
    Rows []Row
}

rowPartial := partial.NewID("row", "templates/row.html")
tablePartial := partial.NewID("table", "templates/table.html").
    SetDot(TablePage{Rows: rows})
tablePartial.With(rowPartial)

target.WithResolver(tablePartial, func(ctx context.Context, r *http.Request, target string) (*partial.Partial, bool) {
    row, ok := findRowForTarget(target)
    if !ok {
        return nil, false
    }
    return partial.NewID(target, "templates/row.html").SetDot(row), true
})

out, err := partial.RenderWithRequest(r.Context(), r, tablePartial)

If the child does not need custom configuration, use WithTemplate as shorthand:

tablePartial := partial.NewID("table", "templates/table.html").
    WithTemplate("templates/row.html").
    SetDot(TablePage{Rows: rows})

This is equivalent to tablePartial.With(partial.NewID("row", "templates/row.html")).

Template Data

In your templates, prefer this model:

  • {{.}}: Your app model when the partial uses SetDot.
  • Typed roots: Additional typed values registered with SetModel or SetContract.
  • {{ctx}}, {{request}}, {{url}}, {{locale}}, {{csrf}}, {{basePath}}: request-aware helpers that stay available when SetDot changes ..

go-partial does not wrap your model in .Data, .App, .Shell, or .Global. Shared application values should be explicit typed roots, for example SetModel(AppInfo) with a matching go-doc declaration. Request-scoped values live behind helper functions so changing dot never hides them.

Concurrency and Template Caching

Configure reusable root partials, functions, render stages, headers, and filesystems before serving requests. Clone before adding request-specific content or dot data. After configuration, partial.RenderWithRequest and partial.Write can be called concurrently on cloned partial trees. Request-specific values such as request, url, ctx, runtime, stage values, selected targets, and template helper bindings are scoped to the active render and are not stored on the reusable partial configuration.

In HTTP handlers, pass the request context, normally r.Context() or a context derived from it. That lets cancellation, deadlines, localization, CSRF state, event sinks, metrics IDs, and other middleware values follow the render. partial.Render(ctx, p) without a request is for tests, offline rendering, and small utilities.

Do not call configuration methods such as SetFunc, Use, With, SetDot, SetFileSystem, SetConnector, or SetResponseHeaders while the same tree is being rendered. Build or clone a separate tree when configuration needs to change at runtime.

Mutable builder and stream/session helpers are intentionally single-owner values. Do not share connector.ResponseBuilder, connector.Trigger, connector.Swap, sse.Writer, or pageflow.SessionData across goroutines without your own synchronization.

The package also includes concurrency safety measures for template caching:

  • Parsed templates are cached by the configured partial tree.
  • Mutexes prevent duplicate parsing for the same Root partial/template/function shape.
  • Cached templates are rebound with request-specific functions per render.
  • Rendered HTML is not cached.
  • Set UseTemplateCache to true to enable parsed template caching.
root.UseTemplateCache(true)

Handling Partial Rendering via HTTP Headers

You can render specific partials based on the X-Target header (or your custom header).

Example:

func handler(w http.ResponseWriter, r *http.Request) {
    page := root.Clone().SetContent(content)
    output, err := partial.RenderWithRequest(r.Context(), r, page)
    if err != nil {
        http.Error(w, "An error occurred while rendering the page.", http.StatusInternalServerError)
        return
    }
    w.Write([]byte(output))
}

To request a specific partial:

curl -H "X-Target: sidebar" http://localhost:8080

Useless benchmark results

with caching enabled

goos: darwin
goarch: arm64
pkg: github.com/donseba/go-partial
cpu: Apple M2 Pro
BenchmarkRenderWithRequest
BenchmarkRenderWithRequest-12    	  526102	      2254 ns/op
PASS

with caching disabled

goos: darwin
goarch: arm64
pkg: github.com/donseba/go-partial
cpu: Apple M2 Pro
BenchmarkRenderWithRequest
BenchmarkRenderWithRequest-12    	   57529	     19891 ns/op
PASS

which would mean that caching is rougly 9-10 times faster than without caching

Contributing

Contributions are welcome! Please open an issue or submit a pull request with your improvements.

License

This project is licensed under the MIT License.

Documentation

Overview

Package partial provides request-aware rendering for Go templates.

Partial trees, Stages, connectors, and template caches are safe to reuse across concurrent renders after configuration is complete. Request-scoped state is carried by RenderContext and is not stored on the reusable Partial configuration.

Configuration methods such as SetFunc, Use, With, SetDot, SetFileSystem, SetConnector, SetResponseHeaders, and extension setup helpers mutate the partial they are called on. Call them before serving requests, or synchronize access externally when changing configuration at runtime.

Index

Constants

View Source
const (
	// EventRenderStart is emitted before a render chain runs.
	EventRenderStart = "render.start"
	// EventRenderFinish is emitted after a render chain succeeds.
	EventRenderFinish = "render.finish"
	// EventRenderError is emitted after a render chain fails.
	EventRenderError = "render.error"
	// EventRenderWriteError is emitted when writing a render response fails.
	EventRenderWriteError = "render.write_error"
	// EventRenderOOBError is emitted when an out-of-band child render fails.
	EventRenderOOBError = "render.oob_error"
	// EventTemplateMissing is emitted when a helper references a missing template.
	EventTemplateMissing = "template.missing"
	// EventTemplateParseError is emitted when parsing or loading templates fails.
	EventTemplateParseError = "template.parse_error"
	// EventTemplateExecuteError is emitted when template execution fails.
	EventTemplateExecuteError = "template.execute_error"
	// EventFuncProtected is emitted when a user function tries to overwrite a protected helper.
	EventFuncProtected = "func.protected"
	// EventContentMissing is emitted when content is called without a content child.
	EventContentMissing = "content.missing"
	// EventTargetMissing is emitted when a requested target cannot be resolved.
	EventTargetMissing = "target.missing"
	// EventContractInvalid is emitted when contract data or helper arguments are invalid.
	EventContractInvalid = "contract.invalid"
)

Variables

This section is empty.

Functions

func Render added in v0.9.0

func Render(ctx context.Context, p *Partial) (template.HTML, error)

Render renders a partial without an http.Request.

Use Render for tests, offline rendering, and jobs that do not have request data. HTTP handlers should usually use Write, or RenderWithRequest when the caller needs the HTML string instead of a written response.

func RenderWithRequest added in v0.9.0

func RenderWithRequest(ctx context.Context, r *http.Request, p *Partial) (template.HTML, error)

RenderWithRequest renders a partial using request-aware connector behavior.

When the connector identifies the request as a partial request, this renders the requested target and appends eligible out-of-band regions. For normal requests it renders the partial itself.

func WithEventSink added in v0.9.0

func WithEventSink(ctx context.Context, sink EventSink) context.Context

WithEventSink attaches a request-scoped event sink to ctx.

Request-scoped sinks are fanned out with partial tree sinks configured with Partial.SetEvents. If the sink owns goroutines, the caller still owns closing it, usually with defer in middleware.

If ctx already has a request-scoped sink, the new sink is appended with fanout.

func Write added in v0.9.0

Write renders a partial and writes the HTTP response.

Write owns the response side of rendering: configured response headers, connector response headers, render-stage response metadata, error fragments, and out-of-band regions are applied here.

Types

type AsyncEvents added in v0.9.0

type AsyncEvents struct {
	// contains filtered or unexported fields
}

AsyncEvents is a non-blocking event dispatcher backed by a bounded queue.

func NewAsyncEvents added in v0.9.0

func NewAsyncEvents(cfg EventsConfig, sinks ...EventSink) *AsyncEvents

NewAsyncEvents returns a non-blocking dispatcher for diagnostic events.

func (*AsyncEvents) Close added in v0.9.0

func (events *AsyncEvents) Close(ctx context.Context) error

Close stops workers after delivering queued events, or returns when ctx ends.

func (*AsyncEvents) Dropped added in v0.9.0

func (events *AsyncEvents) Dropped() uint64

Dropped returns the number of events dropped by this dispatcher.

func (*AsyncEvents) Emit added in v0.9.0

func (events *AsyncEvents) Emit(ctx *RenderContext, event Event)

Emit enqueues event without blocking unless configured with Block.

type DropPolicy added in v0.9.0

type DropPolicy string

DropPolicy decides what AsyncEvents does when its buffer is full.

const (
	// DropNewest drops the incoming event when the queue is full.
	DropNewest DropPolicy = "drop_newest"
	// DropOldest drops one queued event to make room for the incoming event.
	DropOldest DropPolicy = "drop_oldest"
	// Block waits for queue space or Close. Use only when event loss is worse than render latency.
	Block DropPolicy = "block"
)

type Event added in v0.9.0

type Event struct {
	Time         time.Time
	Kind         string
	Level        EventLevel
	Message      string
	Error        error
	Fields       map[string]any
	TraceID      string
	SpanID       string
	ParentSpanID string
	PartialID    string
	ParentID     string
	Name         string
}

Event describes something observable that happened during rendering.

type EventLevel added in v0.9.0

type EventLevel string

EventLevel describes the severity of a diagnostic event.

const (
	// EventDebug is useful for local tracing and lifecycle views.
	EventDebug EventLevel = "debug"
	// EventInfo describes normal diagnostic breadcrumbs.
	EventInfo EventLevel = "info"
	// EventWarn describes recoverable problems or ignored configuration.
	EventWarn EventLevel = "warn"
	// EventError describes render failures or other errors.
	EventError EventLevel = "error"
)

type EventSink added in v0.9.0

type EventSink interface {
	Emit(*RenderContext, Event)
}

EventSink receives diagnostic events. Sinks should be defensive and cheap.

func EventSinkFromContext added in v0.9.0

func EventSinkFromContext(ctx context.Context) EventSink

EventSinkFromContext returns the request-scoped event sink attached to ctx.

type EventSinkFunc added in v0.9.0

type EventSinkFunc func(*RenderContext, Event)

EventSinkFunc adapts a function to EventSink.

func (EventSinkFunc) Emit added in v0.9.0

func (f EventSinkFunc) Emit(ctx *RenderContext, event Event)

Emit sends event to the wrapped function.

type EventsConfig added in v0.9.0

type EventsConfig struct {
	Buffer     int
	DropPolicy DropPolicy
	Workers    int
}

EventsConfig configures AsyncEvents.

type Factory added in v0.10.0

type Factory struct {
	// contains filtered or unexported fields
}

Factory creates request-scoped partials from a configured prototype. The returned values are ordinary *Partial instances and can use the complete native API directly.

func NewFactory added in v0.10.0

func NewFactory(prototype *Partial) *Factory

NewFactory creates a factory from prototype. Later changes to prototype do not affect the factory.

func (*Factory) New added in v0.10.0

func (f *Factory) New(templates ...string) *Partial

New creates a partial with the factory configuration and template paths.

func (*Factory) NewID added in v0.10.0

func (f *Factory) NewID(id string, templates ...string) *Partial

NewID creates a named partial with the factory configuration and template paths.

type FanoutEventSink added in v0.9.0

type FanoutEventSink []EventSink

FanoutEventSink forwards events to multiple sinks.

func FanoutEvents added in v0.9.0

func FanoutEvents(sinks ...EventSink) FanoutEventSink

FanoutEvents returns a sink that forwards events to each non-nil sink.

func (FanoutEventSink) Emit added in v0.9.0

func (sinks FanoutEventSink) Emit(ctx *RenderContext, event Event)

Emit sends event to each configured sink.

type NamedContract added in v0.9.0

type NamedContract interface {
	ContractName() string
}

NamedContract lets values choose their go-doc contract name.

type Partial

type Partial struct {
	// contains filtered or unexported fields
}

Partial stores reusable template, data, and child-tree configuration.

func New

func New(templates ...string) *Partial

New creates a root partial with the default ID "root".

func NewID

func NewID(id string, templates ...string) *Partial

NewID creates a partial with the provided ID.

func (*Partial) ClearDot added in v0.9.0

func (p *Partial) ClearDot() *Partial

ClearDot removes the explicit root value.

func (*Partial) Clone added in v0.2.0

func (p *Partial) Clone() *Partial

Clone returns an independent copy of the partial configuration and child tree.

func (*Partial) Extension added in v0.9.0

func (p *Partial) Extension(key any) (any, bool)

Extension returns extension-owned data from this partial or its parents.

func (*Partial) GetBasePath added in v0.9.0

func (p *Partial) GetBasePath() string

GetBasePath returns the configured base path, falling back to parents.

func (*Partial) ID

func (p *Partial) ID(id string) *Partial

ID sets the stable ID used for target lookup, child registration, and diagnostics.

func (*Partial) IsOOB added in v0.9.0

func (p *Partial) IsOOB() bool

IsOOB reports whether the partial is currently being rendered out-of-band.

func (*Partial) ParentID added in v0.9.0

func (p *Partial) ParentID() string

ParentID returns the ID of the parent partial, if one is attached.

func (*Partial) PartialID added in v0.9.0

func (p *Partial) PartialID() string

PartialID returns the stable render ID for this partial.

func (*Partial) Response added in v0.9.0

func (p *Partial) Response() *connector.ResponseBuilder

Response returns a builder for connector-specific response instructions.

func (*Partial) SetAlwaysSwapOOB added in v0.9.0

func (p *Partial) SetAlwaysSwapOOB(alwaysSwapOOB bool) *Partial

SetAlwaysSwapOOB makes this out-of-band partial render on every partial request.

func (*Partial) SetBasePath added in v0.9.0

func (p *Partial) SetBasePath(basePath string) *Partial

SetBasePath sets the base path for the partial.

func (*Partial) SetConnector added in v0.8.0

func (p *Partial) SetConnector(c connector.Connector) *Partial

SetConnector configures the request connector used by this partial.

func (*Partial) SetContent added in v0.9.0

func (p *Partial) SetContent(child *Partial) *Partial

SetContent registers the primary content child rendered by the content helper.

func (*Partial) SetContract added in v0.9.0

func (p *Partial) SetContract(annotation string, values ...any) *Partial

SetContract registers typed values for go-doc root declarations. Values are matched by type unless they implement NamedContract.

func (*Partial) SetDot added in v0.9.0

func (p *Partial) SetDot(value any) *Partial

SetDot sets the root value passed to html/template Execute. Templates receive this value as "." and can still use core render helpers such as ctx, request, url, basePath, runtime, partial, and content. Extension helpers such as debug, locale, and csrf remain available when their FuncMaps and stages are used.

func (*Partial) SetEvents added in v0.9.0

func (p *Partial) SetEvents(events EventSink) *Partial

SetEvents configures the diagnostic event sink inherited by this partial tree.

func (*Partial) SetExtension added in v0.9.0

func (p *Partial) SetExtension(key any, value any) *Partial

SetExtension stores extension-owned data on this partial.

func (*Partial) SetFileSystem added in v0.2.0

func (p *Partial) SetFileSystem(fs fs.FS) *Partial

SetFileSystem sets the file system for the partial.

func (*Partial) SetFunc added in v0.9.0

func (p *Partial) SetFunc(funcMaps ...template.FuncMap) *Partial

SetFunc registers template functions in the Partial scope.

func (*Partial) SetModel added in v0.9.0

func (p *Partial) SetModel(values ...any) *Partial

SetModel registers values for typed model declarations.

func (*Partial) SetResponse added in v0.9.0

func (p *Partial) SetResponse(response connector.Response) *Partial

SetResponse configures connector-specific response instructions.

func (*Partial) SetResponseHeaders added in v0.8.0

func (p *Partial) SetResponseHeaders(headers map[string]string) *Partial

SetResponseHeaders configures plain HTTP headers written by Write.

func (*Partial) SetStatus added in v0.10.0

func (p *Partial) SetStatus(status int) *Partial

SetStatus configures the HTTP status written by Write. A zero status clears the local value and falls back to the parent partial, then to net/http's default status.

func (*Partial) SetTemplates added in v0.10.0

func (p *Partial) SetTemplates(templates ...string) *Partial

SetTemplates replaces the template paths while preserving the partial's configured filesystem, functions, stages, connector, and cache. It is useful when cloning a configured partial as a request-scoped blueprint.

func (*Partial) TemplatePaths added in v0.9.0

func (p *Partial) TemplatePaths() []string

TemplatePaths returns the template paths configured for this partial.

func (*Partial) Use added in v0.9.0

func (p *Partial) Use(stages ...RenderStage) *Partial

Use appends stages to this partial's render chain.

func (*Partial) UseTemplateCache added in v0.9.0

func (p *Partial) UseTemplateCache(useCache bool) *Partial

UseTemplateCache sets the parsed template cache usage flag for the partial.

func (*Partial) With

func (p *Partial) With(child *Partial) *Partial

With registers a child partial on the partial tree.

Registered children are addressable by ID for partial requests. During a full render, go-partial also includes child templates that are referenced by native Go template calls, such as {{ template "row.gohtml" . }}.

func (*Partial) WithOOB

func (p *Partial) WithOOB(child *Partial) *Partial

WithOOB registers an out-of-band child partial on the partial tree.

func (*Partial) WithTemplate added in v0.9.0

func (p *Partial) WithTemplate(templatePath string) *Partial

WithTemplate creates a child partial from a template path and registers it on the partial tree. The child ID is inferred from the file name without its extension: "templates/sidebar.gohtml" becomes "sidebar".

type RenderContext added in v0.9.0

type RenderContext struct {
	Context  context.Context
	Request  *http.Request
	URL      *url.URL
	BasePath string
	Partial  *Partial
	Runtime  *Runtime
	Kind     RenderKind
	Name     string
	Data     any
	Values   RenderValues
	Error    error
	Response *RenderResponse
	Funcs    template.FuncMap
	Events   EventSink
}

RenderContext contains request-scoped values exposed by the ctx template helper.

func (*RenderContext) Emit added in v0.9.0

func (ctx *RenderContext) Emit(event Event)

Emit sends a diagnostic event through the active render context.

func (*RenderContext) EmitForPartial added in v0.9.0

func (ctx *RenderContext) EmitForPartial(p *Partial, event Event)

EmitForPartial sends an event for a specific partial while preserving the active render context, including request-scoped event sinks.

func (*RenderContext) SetFunc added in v0.9.0

func (ctx *RenderContext) SetFunc(name string, fn any)

type RenderKind added in v0.9.0

type RenderKind string

RenderKind identifies the current render task for render stages.

const (
	// RenderKindPartial is the normal server-side partial render path.
	RenderKindPartial RenderKind = "partial"
	// RenderKindTarget is the core target render path selected by a connector.
	RenderKindTarget RenderKind = "target"
)

type RenderNext added in v0.9.0

type RenderNext func(*RenderContext) (template.HTML, error)

RenderNext calls the next render stage in the chain.

type RenderResponse added in v0.9.0

type RenderResponse struct {
	Headers map[string]string
	Status  int
}

RenderResponse stores generic response metadata set by render stages.

type RenderStage added in v0.9.0

type RenderStage interface {
	Prepare(*RenderContext) (*RenderContext, error)
	Render(*RenderContext, RenderNext) (template.HTML, error)
	Finalize(*RenderContext, template.HTML, error) (template.HTML, error)
}

RenderStage observes or changes a render lifecycle.

Prepare runs before the terminal template render and is the right place to add request-scoped template funcs, resolve context values, or start timing. Render wraps the terminal render and can replace or decorate the produced HTML. Finalize runs after Render, even when Render returned an error, and is the right place for cleanup, metrics, or error response shaping.

type RenderStageHooks added in v0.9.0

type RenderStageHooks struct {
	PrepareFunc  func(*RenderContext) (*RenderContext, error)
	RenderFunc   func(*RenderContext, RenderNext) (template.HTML, error)
	FinalizeFunc func(*RenderContext, template.HTML, error) (template.HTML, error)
}

RenderStageHooks adapts individual lifecycle functions to RenderStage.

func (RenderStageHooks) Finalize added in v0.9.0

func (h RenderStageHooks) Finalize(ctx *RenderContext, out template.HTML, renderErr error) (template.HTML, error)

func (RenderStageHooks) Prepare added in v0.9.0

func (h RenderStageHooks) Prepare(ctx *RenderContext) (*RenderContext, error)

func (RenderStageHooks) Render added in v0.9.0

func (h RenderStageHooks) Render(ctx *RenderContext, next RenderNext) (template.HTML, error)

type RenderValues added in v0.9.0

type RenderValues map[any]any

RenderValues stores request-scoped values shared by render stages and helpers.

func (RenderValues) Clone added in v0.9.0

func (v RenderValues) Clone() RenderValues

func (RenderValues) Get added in v0.9.0

func (v RenderValues) Get(key any) any

func (RenderValues) Set added in v0.9.0

func (v RenderValues) Set(key any, value any)

type Runtime added in v0.9.0

type Runtime struct {
	// contains filtered or unexported fields
}

Runtime is the per-render handle passed explicitly to helpers that need request-aware behavior. It keeps application data typed through SetDot while still giving advanced helpers access to the active request, connector, and partial render path.

func (*Runtime) BasePath added in v0.9.0

func (r *Runtime) BasePath() string

BasePath returns the active render base path.

func (*Runtime) Connector added in v0.9.0

func (r *Runtime) Connector() connector.Connector

Connector returns the connector for the active partial.

func (*Runtime) Context added in v0.9.0

func (r *Runtime) Context() context.Context

Context returns the context for the active render.

func (*Runtime) Partial added in v0.9.0

func (r *Runtime) Partial(path string, args ...any) template.HTML

Partial renders a template path through the current partial tree.

func (*Runtime) RenderContext added in v0.9.0

func (r *Runtime) RenderContext() *RenderContext

RenderContext returns the active render context.

func (*Runtime) RenderPartial added in v0.9.0

func (r *Runtime) RenderPartial(p *Partial) (template.HTML, error)

RenderPartial renders a child partial through the current render stage chain.

func (*Runtime) RenderPartialWithFallback added in v0.9.0

func (r *Runtime) RenderPartialWithFallback(p *Partial) (template.HTML, error)

RenderPartialWithFallback renders a partial through the current render stage chain and returns the configured error fragment when rendering fails.

func (*Runtime) RenderWith added in v0.9.0

func (r *Runtime) RenderWith(kind RenderKind, name string, data any, terminal RenderNext) (template.HTML, error)

RenderWith renders a non-template task through the active render stage chain.

func (*Runtime) Request added in v0.9.0

func (r *Runtime) Request() *http.Request

Request returns the request for the active render, if any.

func (*Runtime) SetValue added in v0.9.0

func (r *Runtime) SetValue(key any, value any)

SetValue stores a value in the active render context.

func (*Runtime) URL added in v0.9.0

func (r *Runtime) URL() *url.URL

URL returns the request URL for the active render, if any.

func (*Runtime) Value added in v0.9.0

func (r *Runtime) Value(key any) any

Value returns a value from the active render context.

Directories

Path Synopsis
examples
docs command
showcase command
exp
actions
Package actions provides experimental render-time action hooks for partials.
Package actions provides experimental render-time action hooks for partials.
csrf
Package csrf provides experimental CSRF token helpers for templates.
Package csrf provides experimental CSRF token helpers for templates.
flash
Package flash provides experimental request-scoped flash message helpers.
Package flash provides experimental request-scoped flash message helpers.
interactions
Package interactions contains optional client-interaction template helpers.
Package interactions contains optional client-interaction template helpers.
localization
Package localization provides experimental locale helpers for templates.
Package localization provides experimental locale helpers for templates.
metrics
Package metrics provides an experimental renderer for passive render metrics.
Package metrics provides an experimental renderer for passive render metrics.
pageflow
Package pageflow contains experimental helpers for multi-step page flows.
Package pageflow contains experimental helpers for multi-step page flows.
selection
Package selection provides experimental selected-partial rendering helpers.
Package selection provides experimental selected-partial rendering helpers.
slots
Package slots provides experimental named regions backed by child partials.
Package slots provides experimental named regions backed by child partials.
sse
Package sse provides experimental server-sent event helpers for streaming rendered partials.
Package sse provides experimental server-sent event helpers for streaming rendered partials.
target
Package target provides experimental dynamic target resolution.
Package target provides experimental dynamic target resolution.
templatehelpers
Package templatehelpers exposes optional convenience helpers for templates.
Package templatehelpers exposes optional convenience helpers for templates.
ext
debug
Package debug provides an optional renderer for debug output.
Package debug provides an optional renderer for debug output.
errors
Package errors provides an optional renderer for render failures.
Package errors provides an optional renderer for render failures.
logger
Package logger adapts go-partial diagnostic events to log/slog.
Package logger adapts go-partial diagnostic events to log/slog.
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL