# A/B tests and flags (/docs/guides/ab-tests)

Sites built on Content can run their A/B tests and feature flags on [Obelism Improve](https://improve.obelism.studio/docs). Instead of hard-coding which content each variant shows, editors write each variant's content in Content, with the built-in **improve block**.

## In the editor [#in-the-editor]

Every blocks field takes it, whatever its allowed blocks, with no content type to create. Under **Add block**, choose **A/B test or flag**: it lists the tests and flags of the same environment in Improve. The block then has a tab per variant, each a list of blocks like the field it is in.

- Each variant takes the blocks its list allows, so a test can only show what the page could show anyway. A variant can't hold another improve block.
- It is offered while the organization has Improve enabled.
- A draft may name a test that is still to come. **Publishing** checks the test or flag exists in that environment, by slug, and that every variant is one of its options. **Promoting** checks the same in the target, so sync the test there in Improve first.

## In content [#in-content]

```json
{
	"_uid": "b_…",
	"_type": "_improve",
	"kind": "test",
	"slug": "homepage-hero",
	"variants": {
		"control": [{ "_uid": "b_…", "_type": "hero", "heading": "Welcome" }],
		"variation": [{ "_uid": "b_…", "_type": "hero", "heading": "Hello there" }]
	}
}
```

`kind` is `test` or `flag`, `slug` the test's or flag's slug, and `variants` one list of blocks per option slug. A multi-variant test's options are its combinations (`red-small`). Agents list what a block can name, with each one's option slugs, with `GET …/improve` (`listImproveSources`).

## On the site [#on-the-site]

The delivery API returns the block as stored, with each variant projected like any other blocks. Ask Improve's SDK for the visitor's option and render that variant:

```ts
import { isImproveBlock, pickImproveVariant } from '@obelism/content'

// `improve` is Improve's SDK (@obelism/improve-sdk), set up for the visitor.
const blocks = fields.body.flatMap((block) => {
	if (!isImproveBlock(block)) return [block]
	const option =
		block.kind === 'flag'
			? improve.getFlagValue(block.slug)
			: improve.getTestValue(block.slug)
	return pickImproveVariant(block, option)
})
```

Improve returns `null` when the test or flag isn't active or the visitor isn't in it. `pickImproveVariant(block, option, fallback?)` then takes the `fallback` option's variant, else the first one.

`npx @obelism/content init` writes an example of the block to `content/examples/` while the organization has Improve on.
