Ultralight comes in several editions, and a few features belong only to the higher ones. Every edition ships the same headers so your codebase can check at compile-time which features are available.

## The Editions

The public download on [our website](https://ultralig.ht/download) is the Free edition. Plus, Pro, and Enterprise are available through a paid license.

Most of the API is identical across editions. The features with an edition floor are:

| Feature | Needs |
|---|---|
| [HTML5 media](/docs/2.0/media-and-audio) (`<video>` and `<audio>`) | Pro or higher |
| [Block-compressed textures](/docs/2.0/compressed-textures) | Pro or higher |
| [The Profiler](/docs/2.0/profiling-and-tracing) | Pro or higher |
| [The GC listener](/docs/2.0/managing-memory) | Pro or higher |
| [Render trace](/docs/2.0/profiling-and-tracing) | Enterprise or higher |
| A custom `ViewConfig::user_agent` | Not in Free |
| The remote inspector | Not in Free |
| [Network request filtering](/docs/2.0/downloads-and-network-control) | Not in Free |

The package itself does not change between editions. You get the same headers and the same four libraries, and public packages ship without debug symbol files.

## Checking at Compile Time

The generated header `<Ultralight/Config.h>` records the edition and the feature flags of the SDK you are building against. `<Ultralight/Defines.h>` includes it, so every Ultralight header already sees these macros (the C API's `<Ultralight/CAPI/CAPI_Defines.h>` includes it directly).

Declarations that belong to a gated feature compile out of the headers in editions without it, so code that touches them needs the same check the headers use.

| Macro | Asks |
|---|---|
| `UL_HAS(X)` | Is feature `X` compiled into this SDK? The edition-gated flags include `MEDIA`, `PROFILER`, `RENDER_TRACE`, `GC_LISTENER`, and `COMPRESSED_TEXTURES`. |
| `UL_EDITION_AT_LEAST(X)` | Is this edition `X` or higher? |
| `UL_EDITION(X)` | Is this edition exactly `X`? |
| `ULTRALIGHT_EDITION_NAME` | The edition as a string (handy in a log line). |

Edition names are `FREE`, `PLUS`, `PRO`, and `ENTERPRISE`. Prefer `UL_HAS` whenever a flag exists for the feature— it is the check the SDK's own headers make.

> 🚧 Edition names must be UPPERCASE
>
> `UL_EDITION_AT_LEAST(Pro)` in an `#if` compiles but tests nothing— the pasted name is undefined, so the check is always true. Write `UL_EDITION_AT_LEAST(PRO)`.

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

///
/// Refuse to build against an edition that lacks what this app needs.
///
#if !UL_EDITION_AT_LEAST(PRO)
#  error "MyApp needs the Pro edition or higher."
#endif

void SetUpConfig() {
  Config config;

  ///
  /// Members that exist only in some editions sit behind the matching
  /// feature check.
  ///
#if UL_HAS(MEDIA)
  config.media_profile = MediaProfile::FavorMemory;
#endif

  Platform::instance().set_config(config);
}
```

## Version Macros

`<Ultralight/Defines.h>` states the version of the headers, and the library reports its own at runtime.

| | Headers | Library |
|---|---|---|
| Ultralight | `ULTRALIGHT_VERSION`, with `ULTRALIGHT_VERSION_MAJOR` / `_MINOR` / `_PATCH` as numbers | `UltralightVersionString()`, with `UltralightVersionMajor()` / `Minor()` / `Patch()` |
| WebKit | `WEBKIT_VERSION` | `WebKitVersionString()` |

Compare the two at startup and you catch a library that does not match the headers you compiled against. From C, `ulVersionString()` and `ulWebKitVersionString()` return the same strings. (What the WebKit number means for your pages is covered in [Supported Web Features](/docs/2.0/supported-web-features).)

The `VERSION.txt` file at the root of the SDK holds the package version, and a trailing `b` marks a beta package. The macros and `UltralightVersionString()` never include that tag.
