Skip to main content
If you find a gap in your testing process that no k6 extension can fix, consider building your own extension.

Before you begin

Anyone who can use the command line to edit files and install software should be able to follow along. However, if you want to create an extension for production use, you should have:
  • Familiarity with both Go (Golang) and JavaScript, and their tooling
  • Understanding of how the Go-to-JavaScript bridge works within k6

Prerequisites

Install the required tools:
1

Install Go

Download and install Go (version 1.19 or higher).
2

Install Git

Install Git for version control.
3

Install xk6

Install the xk6 extension builder:
go install go.k6.io/xk6/cmd/xk6@latest
Verify your installation:
xk6 version

Avoid unneeded work

Before creating a new extension:
  • Check existing extensions - Review the Extensions catalog and the xk6 topic on GitHub
  • Prefer generic solutions - If you can test with a generic protocol like MQTT, use xk6-mqtt over building a custom protocol extension
  • Consider pure JavaScript - A JavaScript library will be better supported, more straightforward, and reusable than an extension

JavaScript extensions

JavaScript extensions extend the JavaScript APIs available to your test scripts. They can add support for new network protocols, improve performance, or add features.

Create a simple extension

1

Set up the project

Create a directory and initialize a Go module:
mkdir xk6-compare
cd xk6-compare
go mod init xk6-compare
2

Write the Go code

Create compare.go with a struct that exposes methods:
package compare

import (
    "fmt"
    "go.k6.io/k6/js/modules"
)

// init is called by the Go runtime at application startup.
func init() {
    modules.Register("k6/x/compare", new(Compare))
}

// Compare is the type for our custom API.
type Compare struct{
    ComparisonResult string // textual description of the most recent comparison
}

// IsGreater returns true if a is greater than b.
func (c *Compare) IsGreater(a, b int) bool {
    if a > b {
        c.ComparisonResult = fmt.Sprintf("%d is greater than %d", a, b)
        return true
    } else {
        c.ComparisonResult = fmt.Sprintf("%d is NOT greater than %d", a, b)
        return false
    }
}
3

Build the extension

Compile k6 with your extension:
xk6 build --with xk6-compare=.
4

Use in a test script

Create test.js:
import compare from 'k6/x/compare';

export default function () {
  console.log(`${compare.isGreater(2, 1)}, ${compare.comparison_result}`);
  console.log(`${compare.isGreater(1, 3)}, ${compare.comparison_result}`);
}
Run the test:
./k6 run test.js
k6 extensions must have the k6/x/ prefix, and the short name must be unique among all extensions built in the same k6 binary.

Advanced module API

For extensions that need access to internal k6 objects (VU state, runtime), implement the advanced module API:
package compare

import (
    "fmt"
    "go.k6.io/k6/js/modules"
)

func init() {
    modules.Register("k6/x/compare", New())
}

type (
    // RootModule is the global module instance.
    RootModule struct{}
    
    // ModuleInstance represents an instance for each VU.
    ModuleInstance struct {
        vu modules.VU
        comparator *Compare
    }
)

var (
    _ modules.Instance = &ModuleInstance{}
    _ modules.Module   = &RootModule{}
)

func New() *RootModule {
    return &RootModule{}
}

func (*RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
    return &ModuleInstance{
        vu: vu,
        comparator: &Compare{vu: vu},
    }
}

type Compare struct{
    vu modules.VU
    ComparisonResult string
}

func (c *Compare) IsGreater(a, b int) bool {
    // Implementation with access to c.vu for VU state
    // ...
}

func (mi *ModuleInstance) Exports() modules.Exports {
    return modules.Exports{
        Default: mi.comparator,
    }
}

Output extensions

Output extensions send metrics to a custom file format or service. They can add custom processing and dispatching methods.

Create an output extension

1

Set up the project

mkdir xk6-output-logger
cd xk6-output-logger
go mod init xk6-output-logger
2

Implement the Output interface

Create logger.go:
package log

import (
    "fmt"
    "io"
    "strings"
    "time"
    
    "go.k6.io/k6/metrics"
    "go.k6.io/k6/output"
)

func init() {
    output.RegisterExtension("logger", New)
}

type Logger struct {
    out io.Writer
}

func New(params output.Params) (output.Output, error) {
    return &Logger{params.StdOut}, nil
}

func (*Logger) Description() string {
    return "logger"
}

func (*Logger) Start() error {
    return nil
}

func (l *Logger) AddMetricSamples(samples []metrics.SampleContainer) {
    for _, sample := range samples {
        all := sample.GetSamples()
        fmt.Fprintf(l.out, "%s [%s]\n", 
            all[0].GetTime().Format(time.RFC3339Nano), 
            metricKeyValues(all))
    }
}

func metricKeyValues(samples []metrics.Sample) string {
    names := make([]string, 0, len(samples))
    for _, sample := range samples {
        names = append(names, fmt.Sprintf("%s=%v", sample.Metric.Name, sample.Value))
    }
    return strings.Join(names, ", ")
}

func (*Logger) Stop() error {
    return nil
}
3

Build and test

xk6 build --with xk6-output-logger=.
./k6 run test.js --out logger --quiet --no-summary --iterations 2

Secret source extensions

Secret source extensions provide secrets to your k6 script at runtime from external sources like HashiCorp Vault or AWS Secrets Manager.

Subcommand extensions

Subcommand extensions add custom CLI commands under the k6 x namespace for setup, validation, and integration tools.

Best practices

Performance

Code in the default function executes many times. Ensure operations are performant and thread-safe.

Initialization

Do heavy initialization in the init context, not in the default function.

Custom metrics

Use Registry.NewMetric() to create custom metrics and emit them with metrics.PushIfNotDone().

Templates

Use the xk6-example or xk6-output-example templates.

Quick start with templates

The fastest way to get started is using GitHub templates:
  1. Create a repository from grafana/xk6-example for JavaScript extensions
  2. Or use grafana/xk6-output-example for output extensions
  3. Open in GitHub Codespaces for instant development environment
  4. Run xk6 run script.js to test immediately

Registry requirements

If you maintain a public xk6 repository and wish to have your extension listed in the registry, review the requirements in the k6 documentation.

Next steps

Bundle Extensions

Build custom k6 binaries with xk6

Explore Extensions

See what others have built

Community Forum

Ask questions and share your extensions

GitHub Template

Start with the official template

Build docs developers (and LLMs) love