Bookmaker Odds Comparison

Bookmaker Odds Comparison

The bookmakerOddsComparison widget renders a cross-bookmaker odds-comparison table for one event, served from the per-event snapshot pre-aggregated by the odds-pipeline-worker. Every price and brand logo is a clickable affiliate link (/go/<cloakedLink>/<channel>[/<trackinglinktarget>]), closed by a BET NOW call to action.

It has two market variants, each documented on its own page with its own examples:

  • 1x2 Match Odds — a single match: one row per bookmaker brand with the 1 / X / 2 prices.
  • Outright Odds — a tournament winner market (e.g. the Premier League Winner): one row per outcome (club or nation), one column per brand, best price highlighted.

The variant is not chosen on the tag: it comes from the market of the config record (1x2, the default, or outright). Everything on this page applies to both.

Key features:

  • Cross-provider odds: prices from several odds providers side by side, one row / column per configured brand. Inactive brands are hidden entirely (no greyed-out rows).
  • Two config approaches: point the widget at a Sanity-managed config with configid (recommended), or pass the config inline as a configuration JSON. configid takes precedence — see below.
  • Localized names + logos: team / competition names render translated (and with logos) for the visitor’s locale when the matching Sanity team / league doc is linked to its odds entity; otherwise the canonical name shows and no logo is used.
  • Header text attributes: 1x2 — optional eventsubtitle / competitionsubtitle; outright — markettitle / markettitlekey (a literal, or a translation key resolved like ctakey; defaults to the market name from the odds data, never empty) and marketsubtitle (empty when absent). Details on each variant page.
  • Affiliate-funnel CTA: every price is a semantic <a> to the brand’s cloaked tracking link. Optional widget-level CTA button text via ctakey / ctatext (ctatext wins; with neither, the button uses the default translation key bookmaker-odds-cta-bet-now, localized for the page language), colours via ctacolors / ctahovercolors.
  • Deep-link anchor: anchorid renders a hidden element with that id inside the widget, so the page can link to #<anchorid> (same convention as the toplists).
  • Tracking-link strategy (tlTarget): switch between the default Vulcan cloaked links and the RBO legacy-id links without changing anything else.

Whichever config approach you use, the display / behaviour attributes — CTA (ctakey / ctatext), ctacolors / ctahovercolors, tlTarget, rel, target, anchorid, maxrows (a display cap on the number of rows — honoured by the outright table, which shows the top N outcomes; the 1x2 layout currently renders every configured brand; use a large value such as 100 to not restrict), the header-text attributes (eventsubtitle / competitionsubtitle for 1x2, markettitle / markettitlekey / marketsubtitle for outright), country, language — always live as top-level attributes on the tag, never inside the configuration JSON. Neither market has a widget title / subtitle setting: a heading above the widget is page (article) content, not part of the element. (rel and target control the affiliate anchors. rel accepts only nofollow or follow — any other value, or omitted, becomes nofollow. target accepts a standard anchor target such as _blank or _self — any other value, or omitted, becomes _blank.)


Configuring the widget: configid vs configuration

There are two ways to give the widget its event + brands, and configid takes precedence over configuration — if both are present, configid wins and configuration is ignored:

Approach Where the config lives Use when
configid (recommended) A Sanity Bookmaker Odds config document, referenced by its slug. internalEventId, market, brands are loaded from Sanity at render time. Editorial-managed placements — editors curate the event + brands in Sanity; the page only carries the slug.
configuration (inline / legacy) A JSON object on the tag: internalEventId, market, brands. Ad-hoc / hard-coded placements, or where no Sanity config document exists.

⚠️ Precedence, and no silent fallback. When configid is present the widget resolves it from Sanity and does not fall back to configuration. A configid that doesn’t resolve renders a clear error (so a wrong slug is obvious) rather than quietly using an inline config. Use one approach per widget.

Localized names + logos are automatic — no attribute needed. When the event’s teams / competition are linked to their odds entity in Sanity (with translations / images), names render localized for the language / country locale and logos become available to the layout; otherwise the widget falls back to the canonical name with no logo.

The tlTarget top-level attribute controls how every affiliate link in the widget (brand logo, each odds cell, and the BET NOW button) is built:

tlTarget Link form Notes
vulcan (default) /go/<cloakedLink>/<channel>[/<trackinglinktarget>]?country=…&language=…&wid=… Standard Vulcan cloaked-link strategy, the same link shape as the toplists. Used when tlTarget is omitted. <channel> is the site’s tracking channel (from the website configuration, se by default); the optional trackinglinktarget attribute (e.g. homepage) is appended after it.
rbo /go/<legacyId>.html RBO legacy strategy. Uses each brand’s legacyId; no query params are appended.
  • The value is case-insensitive (RBO, Rbo, rbo all work) and any unrecognized value falls back to vulcan.
  • For rbo, each brand’s legacyId comes from the brand’s Sanity reference; an inline configuration may override it per brand via the legacyId field of its brands[] entry. If no legacyId can be resolved for a brand, that link safely falls back to the Vulcan form.
  • legacyId is ignored when tlTarget is vulcan, so it is harmless to always include it.

Tag examples per variant: 1x2 Match Odds and Outright Odds.