SEO Meta Tags
⚠️ PRELIMINARY FEATURE - BETA
This feature is currently in beta testing. The API and functionality may change in future releases. We welcome feedback and suggestions as we refine this feature.
Overview
The seoMeta field type allows you to define SEO meta tags at the form/page level. These tags are extracted during Server-Side Rendering (SSR) and injected into the HTML <head> section, enabling:
Search Engine Optimization - Better indexing by Google, Bing, etc.
Social Media Previews - Rich previews when links are shared on Slack, Facebook, LinkedIn, Twitter, Discord, etc.
Dynamic Meta Tags - Use
_calcexpressions to populate tags from database data
Field Structure
Basic Schema
{
"type": "seoMeta",
"metaTags": {
"title": "Page Title - Your Brand",
"description": "Page description for search results",
"og:title": "Social Media Title",
"og:image": "https://yourdomain.com/image.jpg"
}
}Properties
type
String
Yes
Must be "seoMeta"
metaTags
Object
Yes
Meta tag definitions
Behavior
In Editor (GUI)
Visible - Field appears in the form designer in the tree view
Editable - Designers can modify meta tags
No Rendering - Does not render in the actual form
In Production (Runtime)
Hidden - Field does not appear to users
SSR - Meta tags are extracted during SSR for bots/crawlers
Client-Side - Meta tags are also injected into
document.headat runtime for regular users, enabling correct browser tab titles, social sharing from the SPA, and<html lang>attributesMerged - If multiple
seoMetafields exist on the page, theirmetaTagsare merged and later fields override earlier ones
Meta Tags Object
The metaTags object supports two types of values:
1. Static Values
2. Dynamic Values with _calc
Important: The _calc suffix is removed when rendering:
title_calc→ becomestitlein HTMLog:image_calc→ becomesog:imagein HTML
Supported Meta Tag Types
Special Keys
These keys have unique behavior and do not render as <meta> tags:
title
Sets <title> tag (browser tab / search result title)
language
Sets <html lang="..."> attribute. During SSR, defaults to "en" if omitted
_comment*
Ignored — for developer notes and organization
Standard Meta Tags
Use name="" attribute:
Renders as:
Open Graph (Facebook, LinkedIn, Slack)
Use property="" attribute:
Renders as:
Twitter Card
Use name="" attribute:
Renders as:
Article Meta (Blog Posts)
Use property="" attribute:
Product Meta (E-commerce)
Use property="" attribute:
Dynamic Expressions with _calc
Add _calc to any key to evaluate it as a JavaScript expression. The expression has access to the model object from the form's data model.
If both a static key and its _calc counterpart exist, _calc takes precedence.
Complete Examples
Static Page
Dynamic Page (using _calc)
Any key with a _calc suffix is evaluated as a JavaScript expression with access to model. The _calc version takes priority over the static key.
Requirements
Server-Side Rendering (SSR) for crawlers and social previews
The seoMeta field also works in the browser at runtime, but SSR must be enabled if you want bots and social crawlers to receive the tags in the initial HTML response. Meta tags are extracted and rendered server-side when:
A bot/crawler accesses the page (Googlebot, Facebookbot, etc.)
SSR is enabled for the form's layout in site settings
The form is successfully loaded with data
Form Data (onFormRequest)
For dynamic _calc expressions:
The form's
onFormRequesthook must be enabledThe hook must populate the
modelobject with dataSSR will fetch this data automatically
For static values:
No
onFormRequestneededTags are rendered as-is
Validation & Best Practices
Character Limits
title
50-60 characters
description
150-160 characters
og:title
60-90 characters
og:description
55-65 characters
twitter:title
70 characters
Image Requirements
Open Graph (Facebook, LinkedIn, Slack)
1200x630px
1.91:1
Twitter Large Image
1200x675px
16:9
Twitter Summary
120x120px (min)
1:1
Best Practices
✅ Always include fallbacks in
_calcexpressions✅ Use absolute URLs for images
✅ Test previews on actual platforms (Slack, Facebook, etc.)
✅ Keep descriptions concise and compelling
✅ Use high-quality images (1200x630px minimum)
❌ Don't use relative URLs for og:image
❌ Don't exceed character limits (truncated by platforms)
Testing
Test as Bot
Use curl with a bot user-agent:
Look for meta tags in the <head> section.
Test Social Previews
Facebook Debugger: https://developers.facebook.com/tools/debug/
Twitter Card Validator: https://cards-dev.twitter.com/validator
LinkedIn Inspector: https://www.linkedin.com/post-inspector/
Slack: Just paste the link in any channel
Troubleshooting
Meta Tags Not Appearing
Check:
Is SSR enabled for this layout?
Is the form loading successfully?
Are you testing with a bot user-agent?
Does the
seoMetafield exist in the form schema?
Dynamic Values Not Working
Check:
Is
onFormRequestenabled for the form?Is the
modelobject populated with data?Are
_calcexpressions syntactically correct?Check server logs for evaluation errors
Social Previews Not Updating
Solution:
Clear the platform's cache using their debugging tools
Facebook/LinkedIn/Twitter cache previews for 7 days
Use their inspector tools to force a refresh
Security
Escaping
Server-rendered meta tag values are HTML-escaped before they are written into the SSR response:
_calc Expressions
_calc expressions are evaluated with access to the page model, and evaluation failures are caught and logged. Keep these expressions simple and side-effect free.
Auto-Generated Tags
The following are injected automatically if not explicitly defined in metaTags:
<link rel="canonical">
Current page URL
og:url
Current page URL
Page Language
Set the page language with the language key:
Renders as:
For dynamic language based on model data:
If omitted, SSR falls back to "en".
Dynamic robots.txt and sitemap.xml
BetterForms automatically generates robots.txt and sitemap.xml for each tenant.
Static pages with ssr.enabled: true are included automatically.
For dynamic clean URLs such as product detail pages or blog posts, BetterForms can also expand the sitemap through the common onSiteMap hook. This is important because search engines discover and index the clean server-visible URL, not the browser hash URL.
Example onSiteMap response:
Rules:
static SSR layouts are listed automatically
dynamic URLs should be returned by
onSiteMaplocmust be a clean path beginning with/return clean URLs, not hash URLs such as
/#/products/...lastmodis optional
If onSiteMap is not implemented, BetterForms still returns a valid static-only sitemap.
Related Documentation
Feedback
This is a preliminary feature. Please share feedback or suggestions in the Slack channel #suggestions.
Last Updated: February 2026
Last updated
Was this helpful?