PK file format

A PK file is a single-file component format that combines HTML template, Go server logic, CSS, and internationalisation in one file. This page describes every section and attribute.

A single PK file on the left contains five blocks: template, Go script, client TypeScript, scoped style, and i18n JSON. Arrows point to the five compiled outputs on the right: rendered HTML, Go in the binary, browser glue JS, scoped CSS, and typed i18n functions.

File structure

A PK file consists of up to five sections, all optional except the template:

  • <template>: HTML markup with Piko directives (required for the file to render).
  • <script type="application/x-go">: server-side Go code.
  • <script lang="ts">: frontend TypeScript or JavaScript for page-local glue (no reactive state; see about PK files).
  • <style>: component-scoped CSS.
  • <i18n lang="json">: translations, keyed by locale.

Minimal example:

<template>
  <h1>{{ state.Title }}</h1>
</template>

<script type="application/x-go">
package main

import "piko.sh/piko"

type Response struct {
    Title string
}

func Render(r *piko.RequestData, props piko.NoProps) (Response, piko.Metadata, error) {
    return Response{Title: "Hello"}, piko.Metadata{}, nil
}
</script>

<style>
h1 { color: blue; }
</style>

<i18n lang="json">
{
  "en": { "greeting": "Hello" },
  "fr": { "greeting": "Bonjour" }
}
</i18n>

The sections can appear in any order. You can have multiple <style> blocks and multiple <i18n> blocks per file.

Template section

The <template> section contains your HTML markup with Piko directives and interpolation.

Interpolation

Double curly braces interpolate values from the state object, which holds the data returned from Render.

<h1>{{ state.Title }}</h1>
<p>{{ state.Description }}</p>
<span>Count: {{ state.Count }}</span>

For all available directives (p-if, p-for, p-on, p-class, etc.), see directives.

Script section

The <script type="application/x-go"> section contains the page's Go server logic. The parser also accepts type="application/go", lang="go", and lang="golang" as equivalent. internal/sfcparser/dto.go defines the MIME types (MimeGo, MimeGoShort).

Package declaration

The package name is either main or matches the component's directory name.

<script type="application/x-go">
package main
// or
package card
</script>

Response struct

A struct named Response declares the shape of data the template receives via state.

type Response struct {
    Title    string
    Posts    []Post
    UserID   int
    IsAdmin  bool
}

Components that return no data declare piko.NoResponse as the response type.

func Render(r *piko.RequestData, props piko.NoProps) (piko.NoResponse, piko.Metadata, error) {
    return piko.NoResponse{}, piko.Metadata{}, nil
}

Render function

Render is the entry point. It receives request data and props and returns the response value, metadata, and an optional error.

func Render(r *piko.RequestData, props piko.NoProps) (Response, piko.Metadata, error) {
    posts, err := domain.GetRecentPosts(10)
    if err != nil {
        return Response{}, piko.Metadata{}, err
    }

    return Response{
        Title: "Blog",
        Posts: posts,
    }, piko.Metadata{
        Title:       "My Blog",
        Description: "Latest blog posts",
    }, nil
}

RequestData methods

*piko.RequestData exposes the following accessors.

Request metadata:

MethodDescription
Context()Returns the request context
Method()HTTP method (GET, POST, etc.)
Host()Request host
URL()Parsed request URL (defensive copy)
ClientIP()Real client IP resolved by the trusted proxy chain
RequestID()Unique request identifier from the RealIP middleware
Auth()Authentication context, or nil if unauthenticated
CSPTokenAttr()Per-request CSP nonce attribute for inline scripts and styles

Path, query, and form parameters:

MethodDescription
PathParam(key)URL path parameter value
QueryParam(key)First query parameter value
QueryParamValues(key)All query parameter values
FormValue(key)First form field value
FormValues(key)All form field values
PathParams()defensive copy of all path parameters
QueryParams()defensive copy of all query parameters
FormData()defensive copy of all form data
RangePathParams(cb)Iterates path parameters, callback returns false to stop
RangeQueryParams(cb)Iterates query parameters, callback returns false to stop
RangeFormData(cb)Iterates form fields, callback returns false to stop

Cookies:

MethodDescription
Cookie(name)Returns the named cookie or http.ErrNoCookie
Cookies()defensive copy of all request cookies
SetCookie(cookie)Adds a cookie to the HTTP response

Locale and collections:

MethodDescription
Locale()Current request locale
DefaultLocale()Fallback locale
CollectionData()Pre-fetched collection data for p-collection pages
WithCollectionData(data)Returns a copy with the collection data set
WithDefaultLocale(locale)Returns a copy with the default locale set

Internationalisation:

MethodDescription
T(key, fallback...)Global translation lookup
LT(key, fallback...)Local (component-scoped) translation lookup
LF(value)Returns a FormatBuilder for locale-aware formatting

For all metadata fields (SEO, Open Graph, redirects, status codes), see metadata.

Optional functions

The generator recognises three optional top-level functions by exact name: Middlewares, CachePolicy, and SupportedLocales. A function with a different name compiles, and the generator ignores it.

func Middlewares() []string {
    return []string{"auth", "csrf"}
}

func CachePolicy() piko.CachePolicy {
    return piko.CachePolicy{
        Enabled:       true,
        MaxAgeSeconds: 60,
    }
}

func SupportedLocales() []string {
    return []string{"en", "fr", "de"}
}

For props (struct tags, validation, defaults, coercion, query binding), see the passing props to partials how-to.

Style section

The <style> section contains the component's CSS.

<style>
h1 {
    color: var(--g-colour-primary);
    font-size: 2rem;
}

.card {
    background: white;
    border-radius: 0.5rem;
    padding: 1rem;
}
</style>

A file may contain multiple <style> blocks. The compiler concatenates them.

<style>
div { border: 1px solid black; }
</style>
<style>
div { background: white; }
</style>

The scoped attribute scopes styles to the component instance.

<style scoped>
h1 { color: navy; }
</style>

For the <i18n> block and translation functions (T(), LT()), see i18n.

For importing and using partials (import paths, is attribute), see how to layout partials.

Client-side scripts

Additional <script> blocks declare client-side JavaScript.

<script type="application/javascript">
const app = {
    init() {
        console.log('App initialised');
    }
};
</script>

<script type="module">
import { utils } from './utils.js';
export function setup() {
    utils.configure();
}
</script>

Runtime roots in scope

A <script lang="ts"> block in a PK file has three runtime roots available without import:

RootSourcePurpose
pkper-page context, injected by the compilerrefs (pk.refs, pk.createRefs) and lifecycle hooks (pk.onConnected, pk.onDisconnected, pk.onBeforeRender, pk.onAfterRender, pk.onUpdated, pk.onCleanup)
pikoglobal ambient namespaceruntime helpers: piko.bus, piko.partial, piko.partials, piko.nav, piko.form, piko.ui, piko.event, piko.actions, piko.helpers, piko.assets, piko.loader, piko.modal, piko.network, piko.sse, piko.timing, piko.util, piko.trace, piko.autoRefreshObserver, piko.context, piko.hooks, piko.analytics
actiongenerated actions.gen.tstyped server actions, called as action.<package>.<Name>(input).call()

refs is not exposed on the global piko namespace. Use pk.refs (the per-page context) or pk.createRefs(scope) to populate or read element references.

See also