{"id":3755,"date":"2025-12-11T12:56:47","date_gmt":"2025-12-11T11:56:47","guid":{"rendered":"https:\/\/thedatastory.nl\/?p=3755"},"modified":"2025-12-11T12:56:49","modified_gmt":"2025-12-11T11:56:49","slug":"a-scalable-way-to-handle-multiple-ga4-properties-in-dataform","status":"publish","type":"post","link":"https:\/\/thedatastory.nl\/nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/","title":{"rendered":"A scalable way to handle multiple GA4-properties in Dataform"},"content":{"rendered":"\n<p>Many organisations don\u2019t just have one GA4 property \u2013 they have several.&nbsp; A webshop might split brands, countries and domains across different properties, and before you know it you\u2019re maintaining a small fleet of almost-identical setups. The setup works fine, until you need to change something: Add a new metric? Fix a bug in your event model? Adjust a channel grouping? Suddenly you\u2019re touching the same logic in every property-specific SQL file. It\u2019s repetitive, error-prone and it certainly doesn\u2019t scale when \u201ca few properties\u201d slowly turns into \u201cmany\u201d.<\/p>\n\n\n\n<p>In this blog, I\u2019ll walk you through how we tackle this in Dataform by generating SQL through modular functions in JavaScript.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The problem: many properties, many manual changes<\/strong><\/h2>\n\n\n\n<p>A typical GA4 modelling setup in BigQuery often looks like this:<br><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>One flattened events table per property<\/li>\n\n\n\n<li>Multiple sessions models per property&nbsp;&nbsp;<\/li>\n\n\n\n<li>One trends or reporting model per property&nbsp;<\/li>\n<\/ul>\n\n\n\n<p>Structurally, those models are almost identical: they query the same GA4 export schema, apply the same session logic, and use the same channel grouping rules. The main difference between these properties are the dataset name and (possibly) property-specific event parameters.&nbsp;<\/p>\n\n\n\n<p>If everything is written directly in SQL, every change in the pipeline becomes a mini-migration across all properties. For each adjustment you have to individually open each property\u2019s model and edit the SQL.<\/p>\n\n\n\n<p>That\u2019s manageable for two or three properties. It\u2019s far less fun for ten or more, and it becomes a real risk when your analytics landscape keeps evolving, as typos could slip in, or a property could be missed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The solution: JavaScript-generated SQL in `\/includes`<\/strong><br><\/h2>\n\n\n\n<p>Dataform allows you to mix SQL with JavaScript. An `.sqlx` file can contain a `js { &#8230; }` block and embed JavaScript expressions inside the SQL using `${ &#8230; }`. We use this to push all core logic into JavaScript files in the `\/includes` folder, and keep the property-specific models as thin as possible.<\/p>\n\n\n\n<p>A simplified example for an events model might look like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; title: ; notranslate\" title=\"\">\n-- models\/property_1\/base__events_flattened.sqlx\nconfig {\n  type: &quot;incremental&quot;,\n  schema: dataform.projectConfig.vars.ga4_schema\n}\n\njs {\n  require(&quot;includes\/helpers&quot;);\n  var _project          = dataform.projectConfig.vars.project;\n  var _dataset          = dataform.projectConfig.vars.property_1_dataset;\n  var _table_final_days = dataform.projectConfig.vars.table_final_days;\n  var _param_set        = &quot;property_1&quot;;\n}\n\n${ga_events_flattened.base__events_flattened(\n  _project,\n  _dataset,\n  _table_final_days,\n  _param_set\n)}\n<\/pre><\/div>\n\n\n<p>This file doesn\u2019t contain the actual query logic. Instead, it calls a JavaScript function:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ includes\/ga_events_flattened.js\nfunction base__events_flattened(source_project, source_dataset, table_final_days, set_key) {\n  \/\/ build and return a full SQL query as a string\n}\nmodule.exports = { base__events_flattened };\n\n<\/pre><\/div>\n\n\n<p>For another property, say `property_2`, we create almost the same `.sqlx` file, but only change the dataset variable and the parameter key:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nvar _dataset   = dataform.projectConfig.vars.property_2_dataset;\nvar _param_set = &quot;property_2&quot;;\n<\/pre><\/div>\n\n\n<p>The pattern repeats for every step in the pipeline:<br><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>`\/includes\/ga_sessions_traffic_source.js` for sessions and traffic source<\/li>\n\n\n\n<li>`\/includes\/ga_sessions_channelgroup_lnd.js` for channel groupings<\/li>\n\n\n\n<li>`\/includes\/ga_reporting.js` for trend and reporting tables\u00a0<\/li>\n<\/ul>\n\n\n\n<p>Each file exports functions like `base__sessions_traffic_source(&#8230;)` or `reporting_trends(&#8230;)` that return SQL as a string. The models themselves simply plug in the right project, dataset and key. As a result, we have all business logic in one place, and each property-specific model only needs to pass names and identifiers. If at some point we decide to adjust session logic, change how we read GA4 events, or update our trend tables, we do that once in `\/includes` and every property benefits automatically.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Extra customisation: per-property `event_params` in config<\/strong><br><\/h2>\n\n\n\n<p>Of course, GA4 properties aren\u2019t completely identical. One property might track the  parameter `current_location`, whereas another might have `content_group`, and yet another might capture something entirely different. To support this, we add a shared configuration layer for event parameters (and other fields) in a central config file in `\/includes`.<\/p>\n\n\n\n<p>Conceptually, that config looks like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ includes\/config.js\n\nconst event_params_set_property_1 = &#x5B;\n  { type: &quot;int&quot;,    name: &quot;batch_ordering_id&quot; },\n  { type: &quot;string&quot;, name: &quot;campaign_id&quot; },\n  { type: &quot;string&quot;, name: &quot;current_location&quot; }\n];\nconst event_params_set_property_2 = &#x5B;\n  { type: &quot;int&quot;,    name: &quot;batch_ordering_id&quot; },\n  { type: &quot;string&quot;, name: &quot;campaign_id&quot; },\n  { type: &quot;string&quot;, name: &quot;content_group&quot; }\n];\n\/\/ same idea for item_params_set_* and user_properties_set_*\n\n\nmodule.exports = {\n  event_params_set_property_1,\n  event_params_set_property_2,\n  \/\/ ...\n};\n\n<\/pre><\/div>\n\n\n<p>We follow a simple naming convention:<br><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>`event_params_set_&lt;property>`\u00a0<\/li>\n\n\n\n<li>`item_params_set_&lt;property>`\u00a0<\/li>\n\n\n\n<li><code>`user_properties_set_&lt;property><\/code>`<\/li>\n<\/ul>\n\n\n\n<p>The key we pass from the `.sqlx` file (`_param_set = &#8220;property_1&#8243;`) is enough to look up the right configuration inside `ga_events_flattened.js`:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nconst config = require(&quot;includes\/config&quot;);\nconst { generateParamsSQL } = require(&quot;includes\/helpers&quot;);\n\nfunction base__events_flattened(source_project, source_dataset, table_final_days, set_key) {\n  const resolvedEventParams    = config&#x5B;`event_params_set_${set_key}`] || &#x5B;];\n  const resolvedUserProperties = config&#x5B;`user_properties_set_${set_key}`] || &#x5B;];\n  return `\n    SELECT\n      event_date,\n      event_timestamp,\n      user_pseudo_id,\n      ga_session_id,\n      ${generateParamsSQL(resolvedEventParams)}\n      -- plus user_properties, etc.\n    FROM \\`${source_project}.${source_dataset}.events_*\\`\n    WHERE _TABLE_SUFFIX &gt;= FORMAT_DATE(\n      &#039;%Y%m%d&#039;,\n      DATE_SUB(CURRENT_DATE(), INTERVAL ${table_final_days} DAY)\n    )\n  `;\n}\n<\/pre><\/div>\n\n\n<p>A helper like `generateParamsSQL` then turns that configuration into the repetitive SQL needed to safely extract and cast each parameter from `event_params`.<\/p>\n\n\n\n<p>This allows each GA4 property to have its own list of parameters and user properties, all defined in one place.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Keeping the pipeline aligned downstream<\/strong><br><\/h2>\n\n\n\n<p>So far, we\u2019ve focused on the base models. But downstream tables (sessions, channel groupings, trends, dashboards) still need to know which property-specific tables to use.<\/p>\n\n\n\n<p>Two things matter here:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Consistent naming: We keep a clear naming scheme such as `ga_property_1_sessions_intermediate`, `ga_property_2_sessions_intermediate`, and so on, inside each dataset<\/li>\n\n\n\n<li>Explicit dependencies: Even though the SQL is generated by JavaScript, we still declare dependencies in Dataform so the DAG is correct and things run in the right order.<\/li>\n<\/ol>\n\n\n\n<p>Because the SQL is generated in JavaScript, we don\u2019t rely on `ref()` inside SQL strings. Instead, the JavaScript itself uses the consistent naming pattern to address the right tables.<\/p>\n\n\n\n<p>A simplified example for a sessions step might look like this:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ includes\/ga_sessions_traffic_source.js\n\nfunction base__sessions_traffic_source(\n  source_project,\n  source_dataset,\n  source_property\n) {\n  return `\n    SELECT\n      session_date,\n      session_id,\n      traffic_source,\n      medium,\n      campaign\n    FROM\n      \\`${source_project}.${source_dataset}.ga_${source_property}_sessions_intermediate\\`\n  `;\n}\n\nmodule.exports = { base__sessions_traffic_source };\n\n<\/pre><\/div>\n\n\n<p>And the corresponding model for `property_1`:<br><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n-- models\/property_1\/base__sessions.sqlx\nconfig {\n  type: &quot;table&quot;,\n  schema: dataform.projectConfig.vars.ga4_schema,\n  dependencies: &#x5B;&quot;property_1__base__events_flattened&quot;]\n}\n\njs {\n  var _project         = dataform.projectConfig.vars.project;\n  var _dataset         = dataform.projectConfig.vars.property_1_dataset;\n  var _source_property = &quot;property_1&quot;;\n}\n\n${ga_sessions_traffic_source.base__sessions_traffic_source(\n  _project,\n  _dataset,\n  _source_property\n)}\n<\/pre><\/div>\n\n\n<p>A few things to notice here:\u00a0<br><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>dependency<\/strong> on `property_1__base__events_flattened` is declared in the `config` block. That tells Dataform that the sessions table for `property_1` depends on the events table for `property_1`, even though the actual SQL is generated in JavaScript<\/li>\n\n\n\n<li>Inside the JavaScript, we construct the table name using a consistent pattern:\u00a0<br>\u00a0 `ga_${source_property}_sessions_intermediate`.<\/li>\n\n\n\n<li>The `_source_property` string (`&#8221;property_1&#8243;`, `&#8221;property_2&#8243;`, etc.) is the only thing that changes per property; the logic stays the same.<\/li>\n<\/ul>\n\n\n\n<p>Configuration and naming conventions make it easy to target the right property, dependencies make sure everything runs in the right order.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><br><\/h2>\n\n\n\n<p>Working with multiple GA4 properties does not have to mean maintaining multiple parallel SQL codebases.<\/p>\n\n\n\n<p>By:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>recognising the core problem,<\/li>\n\n\n\n<li>moving pipeline logic into JavaScript functions,<\/li>\n\n\n\n<li>capturing per-property differences in a simple `config` file,<\/li>\n\n\n\n<li>and using consistent naming for intermediate tables<\/li>\n<\/ul>\n\n\n\n<p>we end up with a setup that is both flexible and maintainable! New GA4 properties become a matter of adding configuration and a small wrapper, rather than cloning and editing a stack of queries. Because when we refine our modelling logic, we do so once, with confidence that every property stays in sync and every downstream model uses the right inputs. In other words: the number of properties may grow, but the amount of manual work \u2013 and the risk of mistakes \u2013 does not need to grow with it.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Many organisations don\u2019t just have one GA4 property \u2013 they have several.&nbsp; A webshop might split brands, countries and domains across different properties, and before you know it you\u2019re maintaining [&hellip;]<\/p>\n","protected":false},"author":11,"featured_media":3757,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_tribe_ticket_capacity":"0","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[2],"tags":[],"class_list":["post-3755","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-data-stories"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>A scalable way to handle multiple GA4-properties in Dataform - The Data Story<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/thedatastory.nl\/nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/\" \/>\n<meta property=\"og:locale\" content=\"nl_NL\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A scalable way to handle multiple GA4-properties in Dataform - The Data Story\" \/>\n<meta property=\"og:description\" content=\"Many organisations don\u2019t just have one GA4 property \u2013 they have several.&nbsp; A webshop might split brands, countries and domains across different properties, and before you know it you\u2019re maintaining [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/thedatastory.nl\/nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/\" \/>\n<meta property=\"og:site_name\" content=\"The Data Story\" \/>\n<meta property=\"article:published_time\" content=\"2025-12-11T11:56:47+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-12-11T11:56:49+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/thedatastory.nl\/wp-content\/uploads\/2025\/12\/blog_pic.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"217\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"peter\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Geschreven door\" \/>\n\t<meta name=\"twitter:data1\" content=\"peter\" \/>\n\t<meta name=\"twitter:label2\" content=\"Geschatte leestijd\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/\"},\"author\":{\"name\":\"peter\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#\\\/schema\\\/person\\\/9000145fb3f1da82056889d8203a9e26\"},\"headline\":\"A scalable way to handle multiple GA4-properties in Dataform\",\"datePublished\":\"2025-12-11T11:56:47+00:00\",\"dateModified\":\"2025-12-11T11:56:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/\"},\"wordCount\":1017,\"publisher\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/thedatastory.nl\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/blog_pic.jpg\",\"articleSection\":[\"Data stories\"],\"inLanguage\":\"nl-NL\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/\",\"url\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/\",\"name\":\"A scalable way to handle multiple GA4-properties in Dataform - The Data Story\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/thedatastory.nl\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/blog_pic.jpg\",\"datePublished\":\"2025-12-11T11:56:47+00:00\",\"dateModified\":\"2025-12-11T11:56:49+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/#breadcrumb\"},\"inLanguage\":\"nl-NL\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/#primaryimage\",\"url\":\"https:\\\/\\\/thedatastory.nl\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/blog_pic.jpg\",\"contentUrl\":\"https:\\\/\\\/thedatastory.nl\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/blog_pic.jpg\",\"width\":512,\"height\":217},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/data-stories\\\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Data stories\",\"item\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/data-stories\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"A scalable way to handle multiple GA4-properties in Dataform\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/\",\"name\":\"The Data Story\",\"description\":\"Data Analyse, Visualisatie &amp; Automation\",\"publisher\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"nl-NL\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#organization\",\"name\":\"The Data Story\",\"url\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/thedatastory.nl\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/Logo-negatief.svg\",\"contentUrl\":\"https:\\\/\\\/thedatastory.nl\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/Logo-negatief.svg\",\"width\":250,\"height\":49,\"caption\":\"The Data Story\"},\"image\":{\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/thedatastory.nl\\\/en\\\/#\\\/schema\\\/person\\\/9000145fb3f1da82056889d8203a9e26\",\"name\":\"peter\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"nl-NL\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1ac92ff30223b141efd0df83d245b958ebae85d36427934dbe0bf9335990d8ec?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1ac92ff30223b141efd0df83d245b958ebae85d36427934dbe0bf9335990d8ec?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1ac92ff30223b141efd0df83d245b958ebae85d36427934dbe0bf9335990d8ec?s=96&d=mm&r=g\",\"caption\":\"peter\"},\"url\":\"https:\\\/\\\/thedatastory.nl\\\/nl\\\/author\\\/peter\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"A scalable way to handle multiple GA4-properties in Dataform - The Data Story","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/thedatastory.nl\/nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/","og_locale":"nl_NL","og_type":"article","og_title":"A scalable way to handle multiple GA4-properties in Dataform - The Data Story","og_description":"Many organisations don\u2019t just have one GA4 property \u2013 they have several.&nbsp; A webshop might split brands, countries and domains across different properties, and before you know it you\u2019re maintaining [&hellip;]","og_url":"https:\/\/thedatastory.nl\/nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/","og_site_name":"The Data Story","article_published_time":"2025-12-11T11:56:47+00:00","article_modified_time":"2025-12-11T11:56:49+00:00","og_image":[{"width":512,"height":217,"url":"http:\/\/thedatastory.nl\/wp-content\/uploads\/2025\/12\/blog_pic.jpg","type":"image\/jpeg"}],"author":"peter","twitter_card":"summary_large_image","twitter_misc":{"Geschreven door":"peter","Geschatte leestijd":"5 minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/#article","isPartOf":{"@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/"},"author":{"name":"peter","@id":"https:\/\/thedatastory.nl\/en\/#\/schema\/person\/9000145fb3f1da82056889d8203a9e26"},"headline":"A scalable way to handle multiple GA4-properties in Dataform","datePublished":"2025-12-11T11:56:47+00:00","dateModified":"2025-12-11T11:56:49+00:00","mainEntityOfPage":{"@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/"},"wordCount":1017,"publisher":{"@id":"https:\/\/thedatastory.nl\/en\/#organization"},"image":{"@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/#primaryimage"},"thumbnailUrl":"https:\/\/thedatastory.nl\/wp-content\/uploads\/2025\/12\/blog_pic.jpg","articleSection":["Data stories"],"inLanguage":"nl-NL"},{"@type":"WebPage","@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/","url":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/","name":"A scalable way to handle multiple GA4-properties in Dataform - The Data Story","isPartOf":{"@id":"https:\/\/thedatastory.nl\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/#primaryimage"},"image":{"@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/#primaryimage"},"thumbnailUrl":"https:\/\/thedatastory.nl\/wp-content\/uploads\/2025\/12\/blog_pic.jpg","datePublished":"2025-12-11T11:56:47+00:00","dateModified":"2025-12-11T11:56:49+00:00","breadcrumb":{"@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/#breadcrumb"},"inLanguage":"nl-NL","potentialAction":[{"@type":"ReadAction","target":["https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/"]}]},{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/#primaryimage","url":"https:\/\/thedatastory.nl\/wp-content\/uploads\/2025\/12\/blog_pic.jpg","contentUrl":"https:\/\/thedatastory.nl\/wp-content\/uploads\/2025\/12\/blog_pic.jpg","width":512,"height":217},{"@type":"BreadcrumbList","@id":"https:\/\/thedatastory.nl\/data-stories\/a-scalable-way-to-handle-multiple-ga4-properties-in-dataform\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/thedatastory.nl\/en\/"},{"@type":"ListItem","position":2,"name":"Data stories","item":"https:\/\/thedatastory.nl\/en\/data-stories\/"},{"@type":"ListItem","position":3,"name":"A scalable way to handle multiple GA4-properties in Dataform"}]},{"@type":"WebSite","@id":"https:\/\/thedatastory.nl\/en\/#website","url":"https:\/\/thedatastory.nl\/en\/","name":"The Data Story","description":"Data Analyse, Visualisatie &amp; Automation","publisher":{"@id":"https:\/\/thedatastory.nl\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/thedatastory.nl\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"nl-NL"},{"@type":"Organization","@id":"https:\/\/thedatastory.nl\/en\/#organization","name":"The Data Story","url":"https:\/\/thedatastory.nl\/en\/","logo":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/thedatastory.nl\/en\/#\/schema\/logo\/image\/","url":"https:\/\/thedatastory.nl\/wp-content\/uploads\/2021\/11\/Logo-negatief.svg","contentUrl":"https:\/\/thedatastory.nl\/wp-content\/uploads\/2021\/11\/Logo-negatief.svg","width":250,"height":49,"caption":"The Data Story"},"image":{"@id":"https:\/\/thedatastory.nl\/en\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/thedatastory.nl\/en\/#\/schema\/person\/9000145fb3f1da82056889d8203a9e26","name":"peter","image":{"@type":"ImageObject","inLanguage":"nl-NL","@id":"https:\/\/secure.gravatar.com\/avatar\/1ac92ff30223b141efd0df83d245b958ebae85d36427934dbe0bf9335990d8ec?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1ac92ff30223b141efd0df83d245b958ebae85d36427934dbe0bf9335990d8ec?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1ac92ff30223b141efd0df83d245b958ebae85d36427934dbe0bf9335990d8ec?s=96&d=mm&r=g","caption":"peter"},"url":"https:\/\/thedatastory.nl\/nl\/author\/peter\/"}]}},"_links":{"self":[{"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/posts\/3755","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/comments?post=3755"}],"version-history":[{"count":2,"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/posts\/3755\/revisions"}],"predecessor-version":[{"id":3758,"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/posts\/3755\/revisions\/3758"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/media\/3757"}],"wp:attachment":[{"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/media?parent=3755"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/categories?post=3755"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thedatastory.nl\/nl\/wp-json\/wp\/v2\/tags?post=3755"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}