SEO Guide
AngularDart applications are client-side rendered by default, which can make search engine optimization challenging. This guide covers how to make your AngularDart app SEO-friendly.
The Challenge
Single-page applications (SPAs) render all content with JavaScript. Search engine crawlers may not execute JavaScript, so they might not see your content. Solutions include prerendering and proper meta tag management.
Meta Tags with angulardart_seo
The angulardart_seo package provides a service for managing meta tags dynamically from your components.
Installation
Add angulardart_seo to your pubspec.yaml:
dependencies:
angulardart_seo: '>=1.0.4 <2.0.0'
Basic Usage
Inject the SeoService into your component and use it to set meta tags:
import 'package:angulardart/angulardart.dart';
import 'package:angulardart_seo/angulardart_seo.dart';
@Component(
selector: 'about-page',
template: '<h1>About Us</h1><p>...</p>',
directives: [],
)
class AboutPageComponent implements OnInit {
final SeoService _seo;
AboutPageComponent(this._seo);
@override
void ngOnInit() {
_seo.setTitle('About Us - My AngularDart App');
_seo.setDescription('Learn about our company and team.');
_seo.setCanonicalUrl('https://example.com/about');
_seo.setOgTitle('About Us');
_seo.setOgDescription('Learn about our company and team.');
_seo.setOgImage('https://example.com/images/about.jpg');
}
}
Available Methods
| Method | Description |
|---|---|
setTitle(String) |
Sets the page <title> |
setDescription(String) |
Sets the <meta name="description"> |
setCanonicalUrl(String) |
Sets the <link rel="canonical"> |
setOgTitle(String) |
Sets Open Graph title |
setOgDescription(String) |
Sets Open Graph description |
setOgImage(String) |
Sets Open Graph image URL |
setOgType(String) |
Sets Open Graph type (default: website) |
setTwitterCard(String) |
Sets Twitter card type |
setTwitterTitle(String) |
Sets Twitter card title |
setTwitterDescription(String) |
Sets Twitter card description |
setTwitterImage(String) |
Sets Twitter card image |
setRobots(String) |
Sets <meta name="robots"> (e.g., index, follow) |
Dynamic Meta Tags for Routes
Update meta tags when the route changes:
@Component(
selector: 'app-component',
template: '<router-outlet [routes]="routes"></router-outlet>',
directives: [RouterOutlet],
)
class AppComponent implements OnInit {
final Router _router;
final SeoService _seo;
AppComponent(this._router, this._seo);
@override
void ngOnInit() {
_router.onRouteChanged.listen((route) {
switch (route.path) {
case '/':
_seo.setTitle('Home - My App');
_seo.setDescription('Welcome to my AngularDart application.');
break;
case '/about':
_seo.setTitle('About - My App');
_seo.setDescription('Learn more about us.');
break;
}
});
}
}
Sitemap Generation
A sitemap helps search engines discover all pages on your site.
Manual Sitemap
Create a sitemap.xml in your web/ directory:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://example.com/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://example.com/about</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
Automated Sitemap
Use a build script to generate the sitemap from your route definitions:
// tool/generate_sitemap.dart
import 'dart:io';
void main() {
final routes = ['/', '/about', '/blog', '/contact'];
final buffer = StringBuffer();
buffer.writeln('<?xml version="1.0" encoding="UTF-8"?>');
buffer.writeln('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
for (final route in routes) {
buffer.writeln(' <url>');
buffer.writeln(' <loc>https://example.com$route</loc>');
buffer.writeln(' <changefreq>weekly</changefreq>');
buffer.writeln(' </url>');
}
buffer.writeln('</urlset>');
File('web/sitemap.xml').writeAsStringSync(buffer.toString());
}
Run with: dart run tool/generate_sitemap.dart
Robots.txt
Create a robots.txt in your web/ directory:
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
Structured Data (JSON-LD)
Add structured data to help search engines understand your content:
_seo.setJsonLd("""
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "My Company",
"url": "https://example.com",
"logo": "https://example.com/logo.png"
}
""");
SSR + SEO: Server-Aware SeoService
Starting with angulardart_seo 1.3.0, the SeoService is now server-aware. When running on the server (detected via _head == null on the VM), SEO data is stored in a static map and transferred to the client via TransferState.
How It Works
On the browser, SeoService manipulates <title>, <meta>, <link rel="canonical">, Open Graph tags, and Twitter Cards directly in the DOM. On the server, these operations are serialized into TransferState so the data is available during server rendering:
import 'package:angulardart/angulardart.dart';
import 'package:angulardart_seo/angulardart_seo.dart';
@Component(
selector: 'about-page',
template: '<h1>About Us</h1>',
)
class AboutPageComponent implements OnInit {
final SeoService _seo;
AboutPageComponent(this._seo);
@override
void ngOnInit() {
// On the server, this stores in TransferState for SSR rendering.
// On the browser, this sets <title> directly.
_seo.setTitle('About Us - My AngularDart App');
_seo.setDescription('Learn about our company and team.');
// Open Graph tags — also transferred via TransferState on server
_seo.setOgTitle('About Us');
_seo.setOgDescription('Learn about our company and team.');
_seo.setOgImage('https://example.com/images/about.jpg');
// Twitter Card tags
_seo.setTwitterCard('summary_large_image');
_seo.setTwitterTitle('About Us');
_seo.setTwitterDescription('Learn about our company and team.');
// Canonical URL
_seo.setCanonicalUrl('https://example.com/about');
// JSON-LD structured data
_seo.setJsonLd('{\n'
' "@context": "https://schema.org",\n'
' "@type": "Organization",\n'
' "name": "My Company"\n'
'}');
}
}
Adding SEO to an Existing Project
Use the ngdart add seo command:
cd my_existing_app
ngdart add seo
dart pub get
This adds angulardart_seo >=1.3.0 <2.0.0 to your dependencies and configures everything needed for SEO optimization with SSR support.
What Gets Transferred on the Server
| Method | TransferState Key | HTML Output (SSR) |
|---|---|---|
setTitle(title) |
'seo:title' |
<title>...</title> + meta |
setMeta(name, content) |
'seo:meta:$name' |
<meta name="..." content="..."> |
setOgTag(property, content) |
'seo:og:$property' |
<meta property="og:..." content="..."> |
setTwitterTag(name, content) |
'seo:twitter:$name' |
<meta name="twitter:..."> |
setCanonical(url) |
'seo:canonical' |
<link rel="canonical" href="..."> |
setRobots(robots) / setGooglebot(robots) |
'seo:meta:robots' |
<meta name="robots" content="..."> |
setJsonLd(id, data) |
'seo:jsonld:$id' |
<script type="application/ld+json"> |
Best Practices for SSR + SEO
- Set SEO tags in
ngOnInit()— this runs on both server and client, ensuring tags are set during server rendering - Use unique TransferState keys — prefix with your app name to avoid collisions:
'myapp:seo:title' - Combine with
RenderMode.server— for SEO-critical pages (home, about, product listings), userenderMode: RenderMode.serverso the server renders the full HTML with all meta tags - Verify in browser dev tools — after hydration, check that
<head>contains all expected meta tags
Combining with Prerendering
For best SEO results, combine meta tag management with prerendering:
- Use
angulardart_seoto set meta tags per route - Use
angulardart_prerenderto generate static HTML with those tags - Serve prerendered HTML to search engine bots
See Also
- Prerendering Guide — Generate static HTML for SEO
- Deployment Guide — Production deployment strategies