Location Data

Purpose and Scope

Location data is the analytics view that answers where link interactions are coming from. In Dub, the location family is not a separate product surface; it is one set of group-by dimensions inside the broader analytics system for clicks, leads, sales, and sale amount reporting. The dashboard exposes these dimensions as tabs, and the public API exposes them through the same analytics retrieval operation used for other reporting views. This page explains the location-specific dimensions, how they appear in the dashboard, and how the OpenAPI contract represents country, city, region, and continent responses.

Sources: apps/web/ui/analytics/location-section.tsx, apps/web/lib/openapi/analytics/index.ts, apps/web/lib/analytics/types.ts

The reader-facing concept is straightforward: a workspace can ask, for a selected event and time range, which geographical buckets produced the most activity. The official documentation frames country analytics as top countries by event count, including ISO 3166-1 alpha-2 country codes, and shows requests such as event: "clicks", groupBy: "countries", linkId, and interval: "30d". The repository sources align with that model by treating countries, cities, regions, and continents as analytics endpoints and by rendering them in a single LocationSection component.

Sources: apps/web/ui/analytics/location-section.tsx, apps/web/lib/openapi/analytics/index.ts

Core Location Dimensions

Dub’s location dimensions are four related ways to group the same underlying analytics stream. countries is the top-level country view, displayed with a country flag and a country name from COUNTRIES. cities adds a city label and keeps country context, so the UI can show values like a city followed by a region-derived label. regions groups below the country level and falls back to a country label when the region code ends with an unknown marker. continents is the broadest grouping and uses CONTINENTS plus a dedicated continent icon.

Sources: apps/web/ui/analytics/location-section.tsx

The dashboard starts the location card on countries and lets the user switch among Countries, Cities, Regions, and Continents. Each tab has an icon: FlagWavy for countries, OfficeBuilding for cities, LocationPin for regions, and MapPosition for continents. That tab state controls both the analytics query sent by the shared filter hook and the label formatting inside the bar list. This keeps location reporting visually consistent while still allowing each geography level to choose the most useful display name.

Sources: apps/web/ui/analytics/location-section.tsx

The selected business metric is inherited from the analytics context rather than hard-coded in the location card. When the selected analytics tab is sales, the component uses the current saleUnit; otherwise it uses count. That means the same location chart can represent click counts, lead counts, sale counts, or sale amount-style values depending on the surrounding analytics page state. The important implementation detail is that location data is a dimension, while the event and unit determine what is being measured.

Sources: apps/web/ui/analytics/location-section.tsx, apps/web/lib/analytics/types.ts

Relevant Source Files

  • apps/web/ui/analytics/location-section.tsx - Implements the location analytics card, its four geography tabs, selected filter state, query-string updates, display labels, icons, and bar-list rendering.
  • apps/web/app/app.dub.co/(dashboard)/[slug]/links/analytics/client.tsx - Wraps the analytics dashboard area with workspace loading and exceeded-events handling before rendering analytics content.
  • apps/web/lib/analytics/types.ts - Defines shared analytics TypeScript types such as AnalyticsGroupByOptions, AnalyticsResponseOptions, event types, views, sale units, analytics filters, and events filters.
  • apps/web/lib/analytics/utils/index.ts - Re-exports analytics utility modules for CSV conversion, query-string editing, export formatting, interval data, and plan-valid date ranges.
  • apps/web/lib/openapi/analytics/index.ts - Registers the GET /analytics OpenAPI operation, query schema, response union, retrieveAnalytics operation id, and token security requirement.
  • apps/web/ui/analytics/events/index.tsx - Shows how analytics event pages share provider context, selected tabs, upgrade overlays, and plan-aware access messaging for detailed event streams.

Dashboard Execution Flow

The location dashboard flow begins with shared routing and analytics context. LocationSection reads URL state through useRouterStuff, then reads selectedTab and saleUnit from AnalyticsContext. It keeps local state for the active geography tab and for selected filter values that have not yet been applied. When the user changes from countries to cities or another geography level, the component clears the temporary selection. This prevents stale country selections from being accidentally applied to a city, region, or continent query.

Sources: apps/web/ui/analytics/location-section.tsx

Data is loaded through useAnalyticsFilterOption(tab), where tab is one of the geography groupings. The component also requests allData for the same tab with omitGroupByFilterKey: true, which allows the filter UI to reason about all available values even when a particular group-by filter is already active. The card receives dataLength, expandLimit, active-filter state, and clear behavior, then renders the bar list only when data has arrived. If the result set is empty, the card can render the empty state path provided by the analytics card pattern.

Sources: apps/web/ui/analytics/location-section.tsx

Applying a location filter writes to the query string using the singular endpoint name from SINGULAR_ANALYTICS_ENDPOINTS. If no values are selected, the query parameter is deleted. If one or more values are selected, they are joined with commas and stored under the singular key, such as a country-like, city-like, region-like, or continent-like filter key. The active filter values are then derived from searchParams, split back into an array, and used to control the filter UI. This design makes location filters shareable through URLs and consistent with other analytics filter categories.

Sources: apps/web/ui/analytics/location-section.tsx

API Contract and Response Shapes

The public API entry point for location analytics is the analytics retrieval operation at GET /analytics. The OpenAPI module names the operation retrieveAnalytics, gives it the SDK override name retrieve, and describes it as retrieving analytics for a link, a domain, or the authenticated workspace. Its request parameters come from analyticsQuerySchema, and its security requirement is token, so callers should treat API-token authentication as a prerequisite for programmatic location reporting.

Sources: apps/web/lib/openapi/analytics/index.ts

The response schema is a union because the same endpoint serves several analytics families. For location data, the union includes arrays of AnalyticsContinents, AnalyticsCountries, AnalyticsRegions, and AnalyticsCities. The same operation also includes count, timeseries, device, browser, operating system, trigger, referrer, top-link, and top-url response shapes. For consumers, this means groupBy is the key discriminator: a request grouped by countries should be handled as a country array, while cities, regions, and continents each have their own array element shape.

Sources: apps/web/lib/openapi/analytics/index.ts

The shared analytics type layer reinforces that group-by values are constrained by repository constants. AnalyticsGroupByOptions is derived from VALID_ANALYTICS_ENDPOINTS, AnalyticsResponseOptions covers clicks, leads, sales, and saleAmount, and AnalyticsFilters is built from the zod analytics query schema while normalizing date and identifier fields for internal use. Partner and link identifiers can be represented as plain strings or parsed filters, which is useful because analytics queries can originate from API routes, dashboard state, or scheduled partner-profile workflows.

Sources: apps/web/lib/analytics/types.ts

A compact reference for location-oriented requests is therefore: call GET /analytics; provide the required authentication token; choose an event such as clicks, leads, or sales; choose a groupBy value of countries, cities, regions, or continents; scope the request with a link, domain, or workspace-level context as supported by the analytics query schema; and choose an interval or explicit date range. The official SDK examples use dub.analytics.retrieve with event, groupBy, linkId, and interval, matching the OpenAPI operation’s SDK name override.

Sources: apps/web/lib/openapi/analytics/index.ts, apps/web/lib/analytics/types.ts

Filtering, Plan Signals, and Data Quality

Location analytics sits inside plan-aware dashboard boundaries. The analytics client waits for workspace loading to complete, then checks whether the workspace has exceeded its event allowance. If events are exceeded, it renders WorkspaceExceededEvents rather than the analytics children, except for the special case where a Pro workspace is viewing the events page. This is not a location-specific rule, but it affects whether location charts can be reached in the dashboard when a workspace has passed its event limits.

Sources: apps/web/app/app.dub.co/(dashboard)/[slug]/links/analytics/client.tsx

The detailed events stream has a stricter product signal than aggregate location charts. The events UI wraps content in AnalyticsProvider, renders the analytics toggle and events tabs, then passes requiresUpgrade to EventsTable when the plan is free or pro. The upgrade overlay describes a Business-plan real-time stream for clicks, QR code scans, or the selected event type. Use this distinction when explaining analytics access: aggregate grouped analytics and real-time event inspection are adjacent, but they are not the same UI surface.

Sources: apps/web/ui/analytics/events/index.tsx

The analytics utility barrel also hints at the supporting operations around location data. It re-exports CSV conversion, analytics export formatting, query-string editing, interval data calculation, and plan-valid date range helpers. Those modules are not expanded in the supplied evidence, but their export names show that location analytics participates in the same export and date-range machinery as other analytics categories. The official help flow for exporting analytics mentions downloaded CSV files such as countries.csv, which fits the repository’s shared export utility structure.

Sources: apps/web/lib/analytics/utils/index.ts

Geolocation accuracy depends on the IP address Dub receives for the click request. The official help documentation warns that putting a proxy such as Cloudflare Proxy in front of a custom domain can route traffic through the proxy first, causing Dub to see the proxy’s public IP rather than the visitor’s. When a customer reports country or city values that look wrong, check the domain’s proxy configuration before assuming the analytics grouping code is broken. DNS-only configuration is the recommended operational fix in that scenario.

Sources: apps/web/ui/analytics/location-section.tsx

Next Steps

When adding or changing location analytics, start by deciding whether the change belongs to the shared analytics contract or only to dashboard presentation. API-visible changes should be reflected through the analytics query and response schemas consumed by apps/web/lib/openapi/analytics/index.ts and the shared types in apps/web/lib/analytics/types.ts. UI-only changes, such as labels, icons, empty states, or filter behavior, belong in apps/web/ui/analytics/location-section.tsx. For adjacent concepts, read the Analytics API page for endpoint behavior and the Analytics Overview page for the broader data pipeline.