Overview
Symbols and references: two of Builder’s more mysterious features.
I’ve been trying to get to the bottom of what has appeared to me like inconsistent results when using symbols with reference fields for my clients’ projects in Builder. Furthermore, because each feature touches on some of Builder’s more advanced capabilities, testing the two in combination is a good proxy for understanding Builder’s rendering model as a whole.
To better serve my clients and to understand how Builder renders symbols and references, I set up an experiment.
Methodology
I created a Next.js project that renders a Builder page containing a symbol with a reference field on it. The Next.js page fetches the Builder page’s content and renders it during SSR with <BuilderComponent>
.
Additionally, the Next.js page accepts four query parameters, which affect rendering in the following ways:
-
includeRefs=true|false
: sets the content API request’sincludeRefs
option totrue
orfalse
(defaults tofalse
) -
noTraverse=true|false
: sets the content API request’snoTraverse
option totrue
orfalse
(defaults totrue
) -
builderComponentIncludeRefs=true|false
: sets<BuilderComponent>
'sincludeRefs
option totrue
orfalse
(defaults tofalse
) -
builderComponentNoTraverse=true|false
: sets<BuilderComponent>
'snoTraverse
option totrue
orfalse
(defaults totrue
)
These four parameters were chosen because of their importance in affecting the outcome of how a symbol with a reference field would render, according to the documentation and various forum posts.
The most important use case for a symbol with a reference input field is to use the content of the item pointed at by the reference from within the symbol. Since content inputs are consumed within a symbol by accessing properties of state
, I decided to measure the effect of these four parameters on the final value of the symbol’s state.projectRef
property after rendering (projectRef
was the name of the symbol’s reference field). I would observe this value by binding a text block in the symbol to state.projectRef
.
The objective was to try all 16 possible combinations of the query parameters, record how each combination affected the value of state.projectRef
in the symbol, and compare the values to determine which parameters had an effect.
Code
- Back end: GitHub - ersinakinci/builder-repro-react at ersin/repro/single-symbol-reference-field
- Builder test page in Builder: Builder.io: Drag and drop Visual CMS
- Test page is located at http://localhost:3000/one-project.
Methodology
I manually visited the test page in my browser at http://localhost:3000/one-project, passing the values for all 16 combinations of the four test parameters into the query string of the URL.
I copied and pasted the “Project ref (state.projectRef): …” text rendered in the symbol for each combination, formatting the raw results with an online JSON formatting tool and diffing them with an online JSON diffing tool.
I came to the conclusions below by manually looking through the results of the diffs. The formatted JSON output from each combination is listed below under Raw data.
Conclusions
- No content was for the symbol’s reference was inlined unless the containing page’s content API request had
noTraverse: false
. When the content API request hadnoTraverse: false
set, the entire reference field for the symbol was inlined. - Setting
includeRefs
ornoTraverse
on<BuilderComponent>
had no effect. - The results from setting
includeRefs: true, noTraverse: false
vs.includeRefs: false, noTraverse: false
on the Content API request were identical, except for therev
(revision) parameter and the order of the properties.- The impression that different inlined responses varied materially in their content appears to be due to the shifting order of properties in the returned JSON object.
-
Therefore, it appears that
includeRefs
has no effect and only settingnoTraverse
on the content API request has any effect on whether the content for a symbol’s reference field is inlined and passed to that symbol’s state. - No hydration errors were observed during the test.
Caveats
- This experiment only considered the results of rendering a symbol on a page from the perspective of that symbol’s state.
includeRefs
may have some effect on the raw response of the content API request from the page. It’s possible that the symbol itself transforms the content API response received by the page, negating potential differences in the initial payloads when usingincludeRefs: true
vs.includeRefs: false
.
Further questions
- Does
includeRefs
have an effect on the raw content API response that’s not seen in the symbol’s final state? If so, how does the symbol transform the response in such a way thatincludeRefs
doesn’t have an effect on the final result? - Is the content for the symbol’s reference field inlined within the content API response for the page, or is it inlined later—for example, by the symbol?
- What happens if we rerun this experiment with a symbol containing another symbol?
- What happens if we rerun this experiment with a symbol containing a slot containing another symbol?
- What happens if we rerun this experiment using
<BuilderContent>
instead of<BuilderComponent>
, comparing the results of the content parameter of the render function?
Table
Legend
x
: fully-inlined symbol content
o
: no inlined content
Content API includeRefs: true, noTraverse: true
|
Content API includeRefs: true, noTraverse: false
|
Content API includeRefs: false, noTraverse: true
|
Content API includeRefs: false, noTraverse: false
|
|
---|---|---|---|---|
<BuilderComponent> includeRefs: true, noTraverse: true
|
o | x | o | x |
<BuilderComponent> includeRefs: true, noTraverse: false
|
o | x | o | x |
<BuilderComponent> includeRefs: false, noTraverse: true
|
o | x | o | x |
<BuilderComponent> includeRefs: false, noTraverse: false
|
o | x | o | x |
Raw data
Click here
Content API
-
includeRefs
:false
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"data": {
"url": "/projects/example-project-1",
"title": "Example project 1",
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62",
"description": "The first example project."
},
"name": "Example project 1",
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63",
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9",
"meta": {
"lastPreviewUrl": "",
"kind": "data"
},
"rev": "auqykk0flf",
"lastUpdated": 1664820885747,
"testRatio": 1,
"@originModelId": "b594225b65bd44bf887fec35a56754f5",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"published": "published",
"firstPublished": 1664820701357,
"createdDate": 1664820643177,
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"variations": {},
"query": [
{
"value": "/projects/example-project-1",
"property": "urlPath",
"operator": "is",
"@type": "@builder.io/core:Query"
}
]
}
}
Content API
-
includeRefs
:false
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"variations": {},
"data": {
"url": "/projects/example-project-1",
"title": "Example project 1",
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62",
"description": "The first example project."
},
"lastUpdated": 1664820885747,
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9",
"rev": "syb3rds8vbn",
"testRatio": 1,
"name": "Example project 1",
"firstPublished": 1664820701357,
"published": "published",
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63",
"@originModelId": "b594225b65bd44bf887fec35a56754f5",
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"createdDate": 1664820643177,
"query": [
{
"operator": "is",
"value": "/projects/example-project-1",
"@type": "@builder.io/core:Query",
"property": "urlPath"
}
],
"meta": {
"kind": "data",
"lastPreviewUrl": ""
},
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2"
}
}
Content API
-
includeRefs
:false
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"data": {
"url": "/projects/example-project-1",
"title": "Example project 1",
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62",
"description": "The first example project."
},
"name": "Example project 1",
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63",
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9",
"meta": {
"lastPreviewUrl": "",
"kind": "data"
},
"rev": "auqykk0flf",
"lastUpdated": 1664820885747,
"testRatio": 1,
"@originModelId": "b594225b65bd44bf887fec35a56754f5",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"published": "published",
"firstPublished": 1664820701357,
"createdDate": 1664820643177,
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"variations": {},
"query": [
{
"value": "/projects/example-project-1",
"property": "urlPath",
"operator": "is",
"@type": "@builder.io/core:Query"
}
]
}
}
http://localhost:3000/one-project?builderComponentIncludeRefs=true&noTraverse=false
Content API
-
includeRefs
:false
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"meta": {
"kind": "data",
"lastPreviewUrl": ""
},
"testRatio": 1,
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"data": {
"url": "/projects/example-project-1",
"title": "Example project 1",
"description": "The first example project.",
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62"
},
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"createdDate": 1664820643177,
"@originModelId": "b594225b65bd44bf887fec35a56754f5",
"variations": {},
"rev": "0483p1ekkgum",
"firstPublished": 1664820701357,
"name": "Example project 1",
"published": "published",
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9",
"query": [
{
"property": "urlPath",
"@type": "@builder.io/core:Query",
"value": "/projects/example-project-1",
"operator": "is"
}
],
"lastUpdated": 1664820885747,
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63"
}
}
Content API
-
includeRefs
:false
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}
Content API
-
includeRefs
:false
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}
Content API
-
includeRefs
:false
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}
Content API
-
includeRefs
:false
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}
Content API
-
includeRefs
:true
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"@originModelId": "b594225b65bd44bf887fec35a56754f5",
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"firstPublished": 1664820701357,
"meta": {
"kind": "data",
"lastPreviewUrl": ""
},
"variations": {},
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63",
"query": [
{
"@type": "@builder.io/core:Query",
"operator": "is",
"value": "/projects/example-project-1",
"property": "urlPath"
}
],
"name": "Example project 1",
"lastUpdated": 1664820885747,
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"testRatio": 1,
"data": {
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62",
"title": "Example project 1",
"description": "The first example project.",
"url": "/projects/example-project-1"
},
"rev": "wg1fnanwtx",
"createdDate": 1664820643177,
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"published": "published",
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9"
}
}
Content API
-
includeRefs
:true
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"name": "Example project 1",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"published": "published",
"data": {
"url": "/projects/example-project-1",
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62",
"description": "The first example project.",
"title": "Example project 1"
},
"variations": {},
"meta": {
"kind": "data",
"lastPreviewUrl": ""
},
"query": [
{
"operator": "is",
"value": "/projects/example-project-1",
"property": "urlPath",
"@type": "@builder.io/core:Query"
}
],
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63",
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"createdDate": 1664820643177,
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"rev": "zxpi0ibz75m",
"firstPublished": 1664820701357,
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9",
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"lastUpdated": 1664820885747,
"testRatio": 1,
"@originModelId": "b594225b65bd44bf887fec35a56754f5"
}
}
Content API
-
includeRefs
:true
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"firstPublished": 1664820701357,
"query": [
{
"property": "urlPath",
"@type": "@builder.io/core:Query",
"operator": "is",
"value": "/projects/example-project-1"
}
],
"data": {
"url": "/projects/example-project-1",
"title": "Example project 1",
"description": "The first example project.",
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62"
},
"@originModelId": "b594225b65bd44bf887fec35a56754f5",
"name": "Example project 1",
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9",
"variations": {},
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63",
"published": "published",
"lastUpdated": 1664820885747,
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"testRatio": 1,
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"createdDate": 1664820643177,
"rev": "b0mqcbannv",
"meta": {
"lastPreviewUrl": "",
"kind": "data"
}
}
}
Content API
-
includeRefs
:true
-
noTraverse
:false
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project",
"value": {
"createdDate": 1664820643177,
"@originContentId": "b1ebe36a28d0493182bb06e0f7c1775c",
"testRatio": 1,
"lastUpdated": 1664820885747,
"lastUpdatedBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"published": "published",
"createdBy": "7CR6nekYycX9cktjX9sWyiP655l2",
"rev": "ot0i7o45wr",
"@originModelId": "b594225b65bd44bf887fec35a56754f5",
"firstPublished": 1664820701357,
"variations": {},
"@originOrg": "825df0b059fe4a2587c53008d27a7ef9",
"name": "Example project 1",
"data": {
"title": "Example project 1",
"description": "The first example project.",
"url": "/projects/example-project-1",
"image": "https://cdn.builder.io/api/v1/image/assets%2F825df0b059fe4a2587c53008d27a7ef9%2Fe672345ab06d448db71fcc111ca4fd62"
},
"meta": {
"lastPreviewUrl": "",
"kind": "data"
},
"modelId": "bb05894f7ec397f96c4582ee9743040c351408b7da97ee6fd2151eb33d431d63",
"query": [
{
"@type": "@builder.io/core:Query",
"property": "urlPath",
"operator": "is",
"value": "/projects/example-project-1"
}
],
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917"
}
}
Content API
-
includeRefs
:true
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}
Content API
-
includeRefs
:true
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:false
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}
Content API
-
includeRefs
:true
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:false
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}
Content API
-
includeRefs
:true
-
noTraverse
:true
<BuilderComponent>
-
includeRefs
:true
-
noTraverse
:true
{
"@type": "@builder.io/core:Reference",
"id": "22c38c12a88abe0d3e42888b19330658e2a9629ba16f8194e11391f6836f8917",
"model": "project"
}