Using Velo by Wix for dynamic SEO: meta tags, canonical URLs and Open Graph

Module 20: Wix Studio & Velo Advanced SEO | Lesson 225 of 571 | 40 min read

By Michael Andrews, Wix SEO Expert UK

The Wix SEO panel handles static pages well enough, but dynamic pages, those powered by database collections, need programmatic SEO control. When you have hundreds or thousands of product pages, blog posts, or location pages, manually setting meta tags is impossible. The Velo wix-seo API gives you full control over titles, descriptions, canonical URLs, Open Graph tags, and Twitter Cards, all generated dynamically from your data. This lesson covers every method in the API with production-ready code examples.

How-to diagram showing Wix Studio and Velo advanced SEO capabilities including dynamic meta tags, custom schema markup, CMS database pages, multilingual hreflang, and A/B testing
Wix Studio and Velo unlock advanced SEO capabilities that go far beyond what the standard Wix editor provides.

The wix-seo API: Your Programmatic SEO Toolkit

The wix-seo module is a frontend API available on any Wix page where Velo is enabled. It exposes methods for controlling the document head at render time, meaning the tags you set are present when Googlebot crawls the page. This is not client-side-only manipulation; Wix server-side renders the meta tags you set via this API, making them fully visible to search engines.

The primary methods you will use are title for setting the page title, metaTags for setting meta description and other meta elements, links for canonical URLs and alternate language links, and structuredData for JSON-LD schema markup. Each method accepts either a setter function or can be accessed through a combined approach using the seo object.

import wixSeo from 'wix-seo';

$w.onReady(function () {
  wixSeo.title = 'Your Dynamic Page Title | Brand Name';
});

Setting Dynamic Page Titles from Database Content

Page titles are the most important on-page SEO element. For dynamic pages, you want titles that include the specific entity name plus relevant keywords. The pattern is straightforward: query your collection data on page load and construct the title string from the returned fields. Always include your brand name at the end for consistency and brand recognition in search results.

import wixSeo from 'wix-seo';
import wixData from 'wix-data';

$w.onReady(async function () {
  const slug = wixWindow.getRouterData().slug;
  const result = await wixData.query('Products')
    .eq('slug', slug)
    .find();

  if (result.items.length > 0) {
    const product = result.items[0];
    wixSeo.title = product.name + ' - ' + product.category + ' | YourStore';
  }
});
Title Length Tip: Keep dynamically generated titles under 60 characters to avoid truncation in search results. If your product names are long, consider using a shortened version or omitting the category. Test with the formula: product name (max 30 chars) + separator (3 chars) + brand (max 20 chars) = 53 characters, safely under the limit.

Dynamic Meta Descriptions That Convert Clicks

Meta descriptions do not directly influence rankings, but they dramatically affect click-through rates from search results. A compelling, specific description can double your CTR compared to a generic one. For dynamic pages, pull the most persuasive content from your database, typically a short description field, price, or key features, and construct a description that makes searchers want to click.

import wixSeo from 'wix-seo';

$w.onReady(async function () {
  const item = await getCurrentItem();

  wixSeo.metaTags = [
    {
      name: 'description',
      content: item.shortDescription
        ? item.shortDescription.substring(0, 155)
        : 'Discover ' + item.name + ' at competitive prices. Free shipping on orders over $50. Shop now at YourStore.'
    },
    {
      name: 'robots',
      content: 'index, follow, max-image-preview:large'
    }
  ];
});

Notice the fallback pattern in the code above. Not every item in your database will have a populated description field. Always provide a template-based fallback that constructs a reasonable description from the item name and other guaranteed fields. This prevents empty meta descriptions from appearing in search results, where Google would otherwise pull a random snippet from your page content.

Canonical URLs: Preventing Duplicate Content Programmatically

Canonical URLs tell search engines which version of a page is the authoritative one. This is critical for dynamic pages that can be accessed through multiple URL patterns, filtered views, or paginated listings. Without proper canonicalization, Google may split ranking signals across duplicate URLs, weakening all of them.

The wix-seo links property lets you set the canonical link element in the page head. For dynamic pages, the canonical should always point to the clean, parameter-free version of the URL. If your product page can be reached via /products/blue-widget and /products/blue-widget?ref=homepage, the canonical should point to the version without query parameters.

import wixSeo from 'wix-seo';
import wixLocationFrontend from 'wix-location-frontend';

$w.onReady(function () {
  const baseUrl = wixLocationFrontend.baseUrl;
  const path = wixLocationFrontend.path.join('/');
  const canonicalUrl = baseUrl + '/' + path;

  wixSeo.links = [
    {
      rel: 'canonical',
      href: canonicalUrl
    }
  ];
});
Canonical URL Warning: Never set a canonical URL that points to a different page entirely unless you genuinely want to consolidate those pages. A canonical is a strong signal to Google saying "ignore this URL and credit the canonical instead." Incorrect canonicals can remove pages from the index entirely. Always verify your canonical logic with a handful of test URLs before deploying across all dynamic pages.

Open Graph Tags for Social Sharing SEO

Open Graph tags control how your pages appear when shared on Facebook, LinkedIn, and other social platforms. While not a direct ranking factor, social sharing drives traffic and backlinks, both of which influence SEO. Dynamic pages need dynamic Open Graph tags so that each product, article, or listing shows its own image, title, and description when shared.

The og:image tag is especially important. Social posts with rich preview images receive dramatically more engagement than those with generic or missing images. For e-commerce sites, the product image should be the og:image. For blog posts, use the featured image. Always specify og:image:width and og:image:height to help platforms render the preview without an additional image fetch.

import wixSeo from 'wix-seo';

function setOpenGraphTags(item) {
  wixSeo.metaTags = [
    { property: 'og:title', content: item.name + ' | YourStore' },
    { property: 'og:description', content: item.shortDescription || 'Shop ' + item.name + ' at YourStore' },
    { property: 'og:image', content: item.mainImage },
    { property: 'og:image:width', content: '1200' },
    { property: 'og:image:height', content: '630' },
    { property: 'og:type', content: 'product' },
    { property: 'og:url', content: 'https://www.yourstore.com/products/' + item.slug },
    { property: 'og:site_name', content: 'YourStore' },
    { property: 'og:locale', content: 'en_US' }
  ];
}

Twitter Card Tags for Enhanced Tweet Previews

Twitter Cards work similarly to Open Graph but use their own namespace. The most common type for content pages is summary_large_image, which displays a large preview image above the tweet text. While Twitter will fall back to Open Graph tags if Twitter-specific tags are missing, setting both ensures optimal display across all platforms.

function setTwitterCardTags(item) {
  const twitterTags = [
    { name: 'twitter:card', content: 'summary_large_image' },
    { name: 'twitter:title', content: item.name + ' | YourStore' },
    { name: 'twitter:description', content: item.shortDescription || 'Discover ' + item.name },
    { name: 'twitter:image', content: item.mainImage },
    { name: 'twitter:site', content: '@YourStoreHandle' }
  ];
  return twitterTags;
}

Combining All Meta Tags in a Single Page Setup

In practice, you will set all meta tags together during page initialization rather than making separate API calls. The wix-seo API is designed to accept all tag types in a single assignment, which is more efficient and ensures all tags are set before Googlebot completes its render. Below is a comprehensive example that combines everything covered in this lesson into a single, production-ready page setup function.

import wixSeo from 'wix-seo';
import wixData from 'wix-data';
import wixLocationFrontend from 'wix-location-frontend';

$w.onReady(async function () {
  const routerData = wixLocationFrontend.path;
  const productSlug = routerData[routerData.length - 1];

  const result = await wixData.query('Products')
    .eq('slug', productSlug)
    .find();

  if (result.items.length === 0) return;

  const product = result.items[0];
  const fullUrl = wixLocationFrontend.baseUrl + '/products/' + product.slug;
  const description = product.shortDescription
    ? product.shortDescription.substring(0, 155)
    : 'Buy ' + product.name + ' online. Fast shipping and great prices at YourStore.';

  wixSeo.title = product.name + ' - ' + product.category + ' | YourStore';

  wixSeo.metaTags = [
    { name: 'description', content: description },
    { name: 'robots', content: 'index, follow' },
    { property: 'og:title', content: product.name + ' | YourStore' },
    { property: 'og:description', content: description },
    { property: 'og:image', content: product.mainImage },
    { property: 'og:image:width', content: '1200' },
    { property: 'og:image:height', content: '630' },
    { property: 'og:type', content: 'product' },
    { property: 'og:url', content: fullUrl },
    { name: 'twitter:card', content: 'summary_large_image' },
    { name: 'twitter:title', content: product.name + ' | YourStore' },
    { name: 'twitter:description', content: description },
    { name: 'twitter:image', content: product.mainImage }
  ];

  wixSeo.links = [
    { rel: 'canonical', href: fullUrl }
  ];
});

Edge Cases and Troubleshooting

Handling Missing Data Gracefully

Database items will inevitably have missing fields. A product without a description, an event without an image, or a listing without a category are all common scenarios. Your Velo code must handle every null or undefined field with sensible defaults. Never let a meta tag render with "undefined" or "null" as its content, as this looks unprofessional in search results and social previews.

Timing and Server-Side Rendering

The wix-seo API must be called within the $w.onReady function or during the initial page render cycle. If you set meta tags after an asynchronous delay that exceeds the SSR window, the tags may not be present when Googlebot renders the page. Keep your database queries fast by using indexed fields in your .eq() filters and avoid chaining unnecessary .include() references that slow down the query.

Testing Your Dynamic Meta Tags

How to verify your dynamic meta tags are working



Complete How-To Guide: Setting Up Dynamic SEO Meta Tags with Velo

This guide walks you through implementing dynamic page titles, meta descriptions, canonical URLs, and Open Graph tags using the Velo wix-seo API for database-driven Wix pages.

How to implement dynamic SEO tags with the wix-seo API

SSR Timing: The wix-seo API must be called within the $w.onReady function or during the initial render cycle. If your database query takes too long and exceeds the server-side rendering window, the meta tags may not appear to Googlebot. Keep queries fast by using indexed fields and avoid unnecessary .include() references that slow down the query.

This lesson on Using Velo by Wix for dynamic SEO: meta tags, canonical URLs and Open Graph is part of Module 20: Wix Studio & Velo Advanced SEO in The Most Comprehensive Complete Wix SEO Course in the World (2026 Edition). Created by Michael Andrews, the UK's No.1 Wix SEO Expert with 14 years of hands-on experience, 750+ completed Wix SEO projects and 425+ verified five-star reviews.