dvSeoHead function

String dvSeoHead({
  1. required String title,
  2. String? description,
  3. String? siteUrl,
  4. String? image,
  5. String? siteName,
  6. Map<String, String> alternates = const <String, String>{},
  7. String? defaultAlternate,
  8. String type = 'website',
})

Head tags for a page.

Open Graph and Twitter carry the same three facts under different names because the consumers read different tags; a page that sets only one previews as a blank card in whichever client reads the other.

Implementation

String dvSeoHead({
  required String title,
  String? description,
  String? siteUrl,
  String? image,
  String? siteName,
  Map<String, String> alternates = const <String, String>{},
  String? defaultAlternate,
  String type = 'website',
}) {
  final tags = <String>[
    '<title>${_escapeText(title)}</title>',
    _meta('name', 'description', description),
    if (siteUrl != null && siteUrl.isNotEmpty)
      '<link rel="canonical" href="${_escapeAttribute(siteUrl)}">',
    // hreflang, one link per language this page exists in. Without them a
    // site with /fr/pricing and /de/pricing shows a crawler three unrelated
    // pages with the same content, which engines treat as duplicates rather
    // than translations. Written only when there is more than one: a lone
    // hreflang reads as a misconfiguration.
    if (alternates.length > 1)
      for (final MapEntry<String, String> alternate in alternates.entries)
        '<link rel="alternate" hreflang="${_escapeAttribute(alternate.key)}" '
            'href="${_escapeAttribute(alternate.value)}">',
    if (alternates.length > 1 && defaultAlternate != null)
      '<link rel="alternate" hreflang="x-default" '
          'href="${_escapeAttribute(defaultAlternate)}">',
    _meta('property', 'og:title', title),
    _meta('property', 'og:description', description),
    _meta('property', 'og:type', type),
    _meta('property', 'og:url', siteUrl),
    _meta('property', 'og:site_name', siteName),
    _meta('property', 'og:image', _absolute(image, siteUrl)),
    _meta('name', 'twitter:card',
        image == null ? 'summary' : 'summary_large_image'),
    _meta('name', 'twitter:title', title),
    _meta('name', 'twitter:description', description),
    _meta('name', 'twitter:image', _absolute(image, siteUrl)),
  ];

  return tags.where((String tag) => tag.isNotEmpty).join('\n');
}