Zanim zaczniesz
Ten poradnik skupia się na tworzeniu dynamicznych metatagów. Jest to technika, która wymaga podstawowej znajomości Angular SSR (renderowania po stronie serwera) lub SSG (generowania statycznych stron).
Chociaż możesz zaimplementować kod Angulara z tego artykułu, nie poprawi to drastycznie SEO Twojej aplikacji ani nie doda podglądów w mediach społecznościowych, jeśli nie używasz Angular SSR lub SSG. Dzieje się tak, ponieważ Angular SSR jest niezbędny do wygenerowania unikalnych metatagów dla każdej podstrony, na podstawie jej zawartości.
Jeśli renderowanie po stronie serwera w Angularze to dla Ciebie nowy temat, polecam zacząć od kompletnego przewodnika po Angular SSR napisanego przez Miłosza.
Jak tagi Meta i OG wpływają na SEO w Angularze
Zdobywanie ruchu z mediów społecznościowych i komunikatorów jest kluczowe dla wielu biznesów online, w tym platform e-commerce, portali informacyjnych, blogów, wiki, bibliotek treści i wielu innych. W przypadku tego typu aplikacji, gdy link do Twojej strony jest udostępniany, chcesz, aby się wyróżniał. Dlatego kontrolowanie tytułu, opisu i obrazu w podglądzie linku jest absolutną koniecznością, aby zmaksymalizować CTR (współczynnik klikalności).
Używanie metatagów Open Graph (OG) to „złoty standard”, jeśli chodzi o zwiększanie zaangażowania udostępnianych linków. Zanim przejdziemy dalej, spójrz na poniższe przykłady z różnych platform społecznościowych i komunikatorów, aby zobaczyć, jak te tagi wzbogacają podglądy linków o obrazy i dodatkowe metainformacje.


X (Twitter)

Discord

Na każdym przykładzie widać, jak prosty link zyskuje znaczące ulepszenie. Gdy adres URL jest udostępniany, platformy takie jak Facebook czy LinkedIn automatycznie skanują (crawują) tę stronę w poszukiwaniu kluczowych informacji. Używają ich do wygenerowania karty podglądu z dużym obrazem, tytułem i opisem. To sprawia, że cały post jest znacznie bardziej angażujący i zajmuje więcej miejsca na ekranie.
Skoro już widziałeś korzyści płynące z tagów OG, zagłębmy się w standard Open Graph i nauczmy się, jak go implementować.
Czym są tagi OG?
Protokół Open Graph pozwala każdej stronie internetowej stać się „bogatym obiektem” (rich object) w ramach grafu społecznościowego. To jest oficjalna definicja.
Mówiąc prościej, tagi OG to fragmenty kodu HTML <meta> umieszczone w sekcji <head> Twojej strony. Dostarczają one ustrukturyzowane dane platformom społecznościowym, wyszukiwarkom i komunikatorom, definiując kluczowe meta-elementy strony. Dzięki tagom OG udostępniony link wygląda informacyjnie i wizualnie atrakcyjnie, w przeciwieństwie do zwykłego adresu URL.
Zobaczmy przykładową implementację tagów OG w HTML.
Tagi OG w praktyce: Implementacja HTML
Na podstawowym poziomie, dodanie tagów OG do strony oznacza dodanie kilku specjalnych tagów <meta> do jej kodu HTML. Każdy tag używa atrybutów property i content, aby określić konkretną informację, taką jak tytuł, opis czy obraz podglądu.
Spójrzmy na przykład tagów OG w artykule na angular.love. Możesz znaleźć te informacje samodzielnie, klikając prawym przyciskiem myszy na stronie i wybierając opcję „Wyświetl źródło strony”.
<!DOCTYPE html>
<html lang="en">
<head>
...
<meta
property="og:title"
content="Why is inject() better than constructor? - Angular.love"
/>
<meta
property="og:description"
content="Angular.love - a place for all Angular enthusiasts created to inspire and educate."
/>
<meta property="og:type" content="article" />
<meta
property="og:url"
content="https://angular.love/why-is-inject-better-than-constructor"
/>
<meta
property="og:image"
content="https://wp.angular.love/wp-content/uploads/2025/09/Okladki-blog-1920-x-1080-px-28.png"
/>
<meta property="og:image:width" content="1920" />
<meta property="og:image:height" content="1080" />
<!-- Other OG meta tags -->
...
</head>
...
</html>
Chociaż możesz tam zobaczyć wiele tagów, niemal każdy rozbudowany podgląd w mediach społecznościowych jest zbudowany w oparciu o te podstawowe właściwości:
og:title– Nagłówek Twojej treści.og:description– Krótkie, chwytliwe podsumowanie strony (zazwyczaj 1-2 zdania).og:type– Typ treści, np. website, article lub book. Zobacz dokumentację.og:url– Stały, unikalny adres URL strony.og:image– Adres URL obrazu podglądu. To najważniejszy tag przyciągający uwagę!
Te podstawowe tagi tworzą bogaty podgląd Twojej strony. Nie ma jednak jednego standardu walidacji; każda platforma społecznościowa ma własne zasady wyświetlania informacji. Z tego powodu staraj się, aby tytuły i opisy były zwięzłe.
W przypadku obrazu podglądu, przestrzegaj tych kluczowych wytycznych, aby uzyskać najlepsze rezultaty:
Rozmiar: Preferowany rozmiar to1200x630pikseli. Użycie innych proporcji może spowodować niezręczne przycięcie obrazu – a tego nie chcesz.Format: Trzymaj się typów plików JPG lub PNG, aby zapewnić najlepszą kompatybilność na wszystkich platformach.Rozmiar Pliku: Utrzymuj mały rozmiar pliku obrazu. Duży plik może w ogóle nie pojawić się w podglądzie.
Zaimplementowanie tych 5 metatagów wystarczy, aby uzyskać bogaty podgląd na większości platform. Ale czy to oznacza, że w pełni wykorzystałeś moc standardu Open Graph? Jeszcze nie!
Teraz przyjrzyjmy się opcjonalnym tagom, które z pewnością są przydatne dla crawlerów.
Opcjonalne właściwości metadanych OG
Chociaż dodawanie opcjonalnych tagów nie jest ściśle wymagane, jest wysoce zalecane, gdyż dostarczają one crawlerom dodatkowy kontekst.
Informacje o stronie
og:site_name– nazwa Twojej witryny (np. „Angular.love”).og:locale– definiuje lokalizację (język i terytorium) Twojej treści. Stosuje formatlanguage_TERRITORY. Domyślna wartość toen_US.og:locale:alternate– Tablica innych lokalizacji, w których dostępna jest ta strona.
Na przykład:
<meta property="og:site_name" content="Angular.love" />
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="pl_PL" />
<meta property="og:locale:alternate" content="de_DE" />
Metadane obrazu podglądu
og:image:secure_url– Wersja HTTPS adresu URL obrazuog:image:type– Typ MIME obrazu (np. image/jpeg, image/png)og:image:width– Szerokość obrazu w pikselachog:image:height– Wysokość obrazu w pikselachog:image:alt– Opis tego, co znajduje się na obrazie
Pełną listę tagów OG znajdziesz w dokumentacji Open Graph.
Użyj Angular SSR do implementacji tagów OG
Skoro znasz już implementację HTML, czas przenieść to do Angulara. Dobra implementacja zaczyna się od solidnych, silnie typowanych fundamentów, więc naszym pierwszym krokiem jest zdefiniowanie przejrzystego modelu danych.
Zdefiniuj model danych
og-image.ts
export interface OgImage {
url: string;
alt: string;
}
Dla typu OG, wykorzystajmy unię typów string, aby uzyskać autouzupełnianie, zapobiegać literówkom i zapewnić, że nasz SeoService będzie zawsze zgodny z oficjalnym standardem Open Graph.
og-type.ts
export type OgType =
| 'website'
| 'article'
| 'book'
| 'profile'
| 'payment.link'
| 'music.song'
| 'music.album'
| 'music.playlist'
| 'music.radio_station'
| 'video.movie'
| 'video.episode'
| 'video.tv_show'
| 'video.other';
Szybka uwaga na temat og:type i „product”
Być może widziałeś starsze artykuły lub odpowiedzi wygenerowane przez AI, które sugerowały użycie og:type z wartością product. Jednak od 2025 roku wartość product nie jest zdefiniowanym typem w oficjalnej dokumentacji Open Graph i, o ile mi wiadomo, nie jest rozpoznawana przez główne platformy społecznościowe.
Dla stron e-commerce lub list produktów, zalecanym typem jest website. Najlepiej uznać typ product za relikt przeszłości, który został kiedyś stworzony przez Facebooka, a następnie porzucony na rzecz oficjalnej specyfikacji OG.
Kompletny typ SeoData
Teraz, gdy zdefiniowaliśmy nasze podstawowe typy, połączmy je wszystkie. Stwórz typ SeoData, który posłuży jako kompletny typ dla wszystkich informacji o tagach SEO i OG dla danej strony.
seo-data.ts
import type { OgImage } from './og-tags/og-image';
import type { OgType } from './og-tags/og-type';
export type SeoData = {
title?: string;
description?: string;
ogImage?: OgImage;
ogType?: OgType;
ogUrl?: string;
// Other SEO properties...
};
Budowanie serwisu SeoService w Angularze
Gdy nasz model danych jest gotowy, czas zbudować „silnik”, który będzie zarządzał naszymi metatagami. Zamkniemy całą logikę biznesową we wstrzykiwalnym serwisie SeoService.
seo.service.ts
import { DOCUMENT, inject, Injectable } from '@angular/core';
import { Meta } from '@angular/platform-browser';
import { SeoData } from './seo-data.model';
interface ImageParams {
width: number;
height: number;
// See: https://unsplash.com/documentation#supported-parameters
// All possible types for Unsplash image parameters:
format: 'jpg' | 'png' | 'webp';
fit: 'clip' | 'crop' | 'fill' | 'facearea' | 'fit' | 'scale';
}
/*
Open Graph image requirements:
- size: 1200x630
- format: jpg or png
*/
export const OG_IMAGE_PARAMS: ImageParams = {
width: 1200,
height: 630,
format: 'jpg',
fit: 'crop',
};
@Injectable({ providedIn: 'root' })
export class SeoService {
// Other SEO logic...
private readonly _metaService = inject(Meta);
setSeoData(seoData: SeoData): void {
const title = seoData.title ? `${seoData.title} | SSRmart` : 'SSRmart';
// Other SEO logic...
this._updateMetaTag('og:locale', 'en_US');
this._updateMetaTag('og:site_name', 'SSRmart');
this._updateMetaTag('og:title', title);
this._updateMetaTag('og:description', seoData.description);
this._updateMetaTag(
'og:image',
this._getImageParamsUrl(seoData.ogImage?.url, OG_IMAGE_PARAMS)
);
this._updateMetaTag('og:image:width', '1200');
this._updateMetaTag('og:image:height', '630');
this._updateMetaTag('og:image:type', 'image/jpeg');
this._updateMetaTag('og:image:alt', seoData.ogImage?.alt);
this._updateMetaTag('og:url', seoData.ogUrl);
this._updateMetaTag('og:type', seoData.ogType);
// Other SEO logic...
}
private _updateMetaTag(name: string, content?: string): void {
if (!content) {
this._metaService.removeTag(`property="${name}"`);
return;
}
if (this._metaService.getTag(`property="${name}"`)) {
this._metaService.updateTag({ property: name, content });
} else {
this._metaService.addTag({ property: name, content });
}
}
private _getImageParamsUrl(
imageUrl: string | undefined,
imageParams: ImageParams
): string | undefined {
if (!imageUrl) return undefined;
const url = new URL(imageUrl);
url.search = '';
url.searchParams.set('w', imageParams.width.toString());
url.searchParams.set('h', imageParams.height.toString());
url.searchParams.set('fm', imageParams.format);
url.searchParams.set('fit', imageParams.fit);
return url.toString();
}
}
1. Konfiguracja obrazu
Na górze pliku zdefiniowałem interfejs ImageParams i stałą OG_IMAGE_PARAMS. Stała ta reprezentuje idealne ustawienia obrazu OG (1200×630, jpg).
2. Metody pomocnicze
_updateMetaTag() – podstawowe narzędzie do interakcji z sekcją <head> dokumentu. Używa wbudowanego serwisu Meta z Angulara, aby dodać, zaktualizować lub usunąć tag. Zapobiega to pojawianiu się zduplikowanych metatagów w kodzie HTML.
_getImageParamsUrl() – przyjmuje bazowy adres URL obrazu i obiekt ImageParams, aby skonstruować finalny, idealnie sformatowany adres URL obrazu z Unsplash. W tym przykładzie logika jest dostosowana do API Unsplash, ale trzymając ją w osobnej metodzie, można ją łatwo dostosować do dowolnego innego CDN-a z obrazami. Uwaga: w mojej aplikacji wszystkie obrazy pochodzą z CDN Unsplash.
3. Publiczne API: setSeoData()
To jest publiczne API, z którym będzie wchodzić w interakcję reszta naszej aplikacji. Akceptuje nasz silnie typowany obiekt SeoData i ustawia odpowiednie tagi OG w sekcji <head> dokumentu. Ta metoda obsługuje również ustawianie domyślnych wartości dla tagów takich jak og:site_name i og:locale.
Implementacja SeoService na Statycznej Stronie
Skoro nasz SeoService jest gotowy, użyjmy go w HomePageComponent. Najpierw zdefiniujemy funkcję, która zwraca nasz statyczny obiekt konfiguracji SEO:
home-page-seo.ts
import { inject } from '@angular/core';
import { SeoData } from '@ssrmart/client/utils';
import { ConfigService } from '@ssrmart/shared/config';
export const getHomePageSeo = (): SeoData => {
const baseUrl = inject(ConfigService).get('baseUrl');
return {
title: 'Welcome to SSRmart - Your Online Shopping Destination',
description:
'Discover amazing products at great prices. Shop the latest trends in electronics. Fast shipping and excellent customer service.',
ogType: 'website',
ogUrl: baseUrl,
ogImage: {
url: 'https://images.unsplash.com/photo-1498049794561-7780e7231661',
alt: 'Desk with laptop, headphones, smartphone, and smartwatch',
},
};
};
Następnie wykorzystamy tę funkcję w naszym HomePageComponent:
home-page.component.ts
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { SeoService } from '@ssrmart/client/utils';
import { getHomePageSeo } from './home-page-seo';
@Component({
selector: 'ssrmart-home-page',
templateUrl: './home-page.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
// Component imports...
],
})
export class HomePageComponent {
// Component logic...
constructor() {
inject(SeoService).setSeoData(getHomePageSeo());
}
}
HomePageComponent to prosty przykład dla strony statycznej. W przypadku stron dynamicznych, takich jak strona produktu czy artykułu, będziesz musiał najpierw pobrać dane, zanim ustawisz tagi OG. Najlepszym sposobem na to jest użycie resolvera routingu lub efektu. Jeśli interesuje Cię taka implementacja, czytaj dalej.
Aby jeszcze bardziej ulepszyć ten kod, mógłbyś stworzyć niestandardowy wstrzykiwacz (injector), który akceptowałby SeoData jako parametr. Taki wstrzykiwacz automatycznie wstrzykiwałby SeoService i ustawiał dane. Przykład takiego wzorca znajdziesz w funkcji injectNavigationEnd z biblioteki ngxtension.
Jak sprawdzić, czy Twoje tagi OG działają?
Gdy skończyliśmy implementację podstawowego kodu, przetestujmy, czy wszystko działa poprawnie.
Inspekcja Ręczna
Zacznijmy od sprawdzenia, czy tagi renderują się w naszej odpowiedzi HTML. Aby to zrobić:
- Kliknij prawym przyciskiem myszy na stronie, wybierz opcję
Wyświetl źródło strony. - Otworzy się nowa karta z odpowiedzią HTML strony. Skopiuj wszystko za pomocą
Ctrl+AorazCtrl+C. - Wklej skopiowany kod do nowego, pustego pliku w swoim IDE, wybierz tryb języka HTML i sformatuj go, aby poprawić czytelność odpowiedzi HTML.
W przypadku mojej aplikacji, jak widać poniżej, tagi zostały wygenerowane poprawnie. Twoja odpowiedź HTML powinna wyglądać podobnie.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Other head tags ... -->
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="SSRmart" />
<meta
property="og:title"
content="Welcome to SSRmart - Your Online Shopping Destination | SSRmart"
/>
<meta
property="og:description"
content="Discover amazing products at great prices. Shop the latest trends in electronics. Fast shipping and excellent customer service."
/>
<meta
property="og:image"
content="https://images.unsplash.com/photo-1498049794561-7780e7231661?w=1200&h=630&fm=jpg&fit=crop"
/>
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:type" content="image/jpeg" />
<meta
property="og:image:alt"
content="Desk with laptop, headphones, smartphone, and smartwatch"
/>
<meta property="og:url" content="https://ssrmart.vercel.app" />
<meta property="og:type" content="website" />
<!-- Styles ... -->
</head>
<body class="mat-typography">
<!-- Body Content ... -->
</body>
</html>
Używanie narzędzi online do walidacji odpowiedzi Angular SSR
Jak możesz sobie wyobrazić, ręczne sprawdzanie HTML dla każdej strony nie jest efektywne do walidacji tagów OG. Na szczęście istnieje wiele platform, które oferują darmowe narzędzia do tego celu. Te „inspektory postów” przeskanują Twoją stronę „na żywo” i pokażą Ci dokładnie, jaki podgląd wygenerują.
Przykładowe aplikacje webowe to:
- Oficjalny walidator postów LinkedIn
- Oficjalny walidator postów Meta
- Metatags.io – pokazuje podglądy tagów OG dla wszystkich głównych platform
Możesz także spróbować sprawdzić tagi OG za pomocą rozszerzenia do Chrome SEO META in 1 CLICK.
Aby zwalidować swoją stronę, po prostu wklej jej adres URL do narzędzia. Zweryfikują one Twoją odpowiedź HTML i wskażą błędy, jeśli brakuje jakiegoś tagu lub obraz jest nieprawidłowo sformatowany. Te walidatory powinny być pierwszym krokiem w debugowaniu, dlaczego rozbudowany podgląd Twojej strony w mediach społecznościowych nie działa.
Walidator Postów Linkedin
Oto zrzut ekranu z Inspektora Postów LinkedIn analizującego stronę główną naszej aplikacji SSRmart:


Możesz zauważyć, że inspektor LinkedIn raportuje typ strony og:type jako Article, mimo że jawnie ustawiliśmy go na website. Wygląda na to, że jest to błąd w crawlerze LinkedIn – w chwili pisania tego tekstu, wyświetla on typ Article dla niemal każdej strony, którą próbuję przeskanować.
Oprócz pokazywania zwalidowanych danych i ścieżki przekierowań, walidator postów LinkedIn dostarcza sugestie dotyczące alternatywnych tytułów, opisów lub znalezionych obrazów, których możemy użyć w tagach OG.
Inspektor Postów Meta
Oto wyniki z walidatora Meta:


Jak widać, debugger udostępniania Facebooka nie wskazuje żadnych błędów dla witryny SSRmart.
Uwaga: nawet jeśli narzędzia dadzą Ci „zielone światło”, zawsze sam przetestuj podgląd w mediach społecznościowych. Jedynym sposobem, aby być w 100% pewnym, jest skopiowanie linku i wklejenie go bezpośrednio do wersji roboczej posta lub wiadomości na LinkedIn, Discordzie czy Facebooku. Pozwoli Ci to zobaczyć dokładnie to, co zobaczą Twoi użytkownicy, zanim udostępnisz to szerzej.
W przypadku większych projektów możesz nawet zautomatyzować ten proces. Istnieją usługi, które mogą automatycznie sprawdzać Twoje tagi OG za każdym razem, gdy aktualizujesz kod, wyłapując wszelkie problemy, zanim Twoje zmiany trafią na produkcję.
Dodatkowe tagi – Standard Twitter Tags
W idealnym świecie mielibyśmy tylko jeden standard dla podglądów społecznościowych. Ale, jak można się spodziewać, tak nie jest. Oprócz standardu OG, musisz także zaimplementować Tagi Twittera, znane również jako Twitter Cards.
Tagi te są używane przez X (dawniej Twitter) do generowania własnych, rozbudowanych podglądów w formie kart. Poniżej widać przykład dużej karty Twittera.

Implementacja tagów Twittera w HTML
Implementacja tagów Twittera w HTML jest równie prosta jak implementacja tagów OG – używasz tagów <meta> z innymi nazwami właściwości (property).
Oto najważniejsze z nich:
twitter:site– nazwa użytkownika @ witryny na Twitterze.twitter:site:id– ID użytkownika. Uwaga: wymagany jest albotwitter:site, albotwitter:site:id– wystarczy zaimplementować jeden z nichtwitter:card– Definiuje typ karty. Dostępne wartości to summary, summary_large_image, player, app.twitter:title– Tytuł pokazywany w podglądzie tweeta.
twitter:description– Krótki opis pojawiający się pod tytułem.twitter:image– Adres URL obrazu podglądu.- Zalecany rozmiar obrazu to 1200×600. Bądź ostrożny: to proporcje 2:1 w porównaniu do standardu obrazu OG (1200×630). Jeśli użyjesz tego samego obrazu dla obu tagów, zawartość może zostać przycięta!
- Minimalne wymiary: 300×157
- Maksymalne wymiary: 4096px x 4096px
- Maksymalny rozmiar pliku to 5MB
twitter:image:alt– Tekst alternatywny dla obrazu.
Listę innych dostępnych tagów Twittera znajdziesz w dokumentacji X dla deweloperów.
Poniżej widać przykład, jak tagi te mogą wyglądać w odpowiedzi HTML.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Other head tags ... -->
<meta property="twitter:site" content="@ssrmart" />
<meta property="twitter:card" content="summary_large_image" />
<meta
property="twitter:title"
content="Welcome to SSRmart - Your Online Shopping Destination | SSRmart"
/>
<meta
property="twitter:description"
content="Discover amazing products at great prices. Shop the latest trends in electronics. Fast shipping and excellent customer service."
/>
<meta
property="twitter:image"
content="https://images.unsplash.com/photo-1498049794561-7780e7231661?w=1200&h=600&fm=jpg&fit=crop"
/>
<meta
property="twitter:image:alt"
content="Desk with laptop, headphones, smartphone, and smartwatch"
/>
<!-- Styles ... -->
</head>
<body class="mat-typography">
<!-- Body Content ... -->
</body>
</html>
Dodawanie rozbudowanego podglądu linku w Slacku
W przeszłości Twitter wspierał dodatkowe tagi klucz-wartość dla swojej wycofanej już karty produktu (Product Card). Chociaż X już nie używa tych tagów, ich renderowanie jest nadal cenne, ponieważ platformy takie jak Slack używają ich do tworzenia bogatych, dwukolumnowych podglądów.
Tagi działają w parach:
twitter:label1– Etykieta dla pierwszego wierszatwitter:data1– Wartość dla pierwszego wierszatwitter:label2– Etykieta dla drugiego wierszatwitter:data2– Wartość dla drugiego wiersza
To świetny sposób na dodanie dodatkowego kontekstu „na pierwszy rzut oka”. Popularne przykłady to: Czas czytania artykułu, Nazwa autora, Cena produktu i Dostępność produktu (np. „W magazynie”).
Oto jak można by je zaimplementować w kodzie HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Other head tags ... -->
<meta property="twitter:label1" content="Reading Time" />
<meta property="twitter:data1" content="8 min" />
<meta property="twitter:label2" content="Published At" />
<meta property="twitter:data2" content="January 15, 2024" />
<!-- Styles ... -->
</head>
<body class="mat-typography">
<!-- Body Content ... -->
</body>
</html>
Tworzy to czysty, dwukolumnowy podgląd w Slacku, jak widać poniżej:

Użyj SSR do renderowania tagów Twitter/X w Angularze
Skoro rozumiemy już HTML dla tagów Twittera i ich wpływ na podglądy, zintegrujmy je z naszym SeoService.
Zdefiniuj model danych dla Twittera
Najpierw tworzymy TwitterCardType, aby mieć silnie typowaną implementację.
twitter-card-type.ts
export type TwitterCardType =
| 'summary'
| 'summary_large_image'
| 'app'
| 'player';
Następnie rozszerzymy nasz model danych o nowe właściwości konfiguracyjne.
seo-data.model.ts
import type { KeyValue } from '@angular/common';
import type { OgImage } from './og-tags/og-image';
import type { OgType } from './og-tags/og-type';
import type { TwitterCardType } from './twitter-tags/twitter-card-type';
export type SeoData = {
title?: string;
description?: string;
ogImage?: OgImage;
ogType?: OgType;
ogUrl?: string;
twitterCardType?: TwitterCardType;
twitterLabel?: KeyValue<string, string>;
twitterLabel2?: KeyValue<string, string>;
// Other SEO properties...
};
Następnie zaktualizujemy logikę SeoService, aby ustawiała te nowe tagi.
import { DOCUMENT, inject, Injectable } from '@angular/core';
import { SeoData } from './seo-data.model';
import type { TwitterCardType } from './twitter-tags/twitter-card-type';
import { Meta } from '@angular/platform-browser';
interface ImageParams {
width: number;
height: number;
// See: https://unsplash.com/documentation#supported-parameters
// All possible types for Unsplash image parameters:
format: 'jpg' | 'png' | 'webp';
fit: 'clip' | 'crop' | 'fill' | 'facearea' | 'fit' | 'scale';
}
/*
Open Graph image requirements:
- size: 1200x630
- format: jpg or png
*/
export const OG_IMAGE_PARAMS: ImageParams = {
width: 1200,
height: 630,
format: 'jpg',
fit: 'crop',
};
/*
Twitter image requirements:
- min size: 300x157, max size: 4096x4096
- aspect ratio: 2:1
- format: jpg, png, webp, gic
*/
export const TWITTER_IMAGE_PARAMS: ImageParams = {
width: 1200,
height: 600,
format: 'jpg',
fit: 'crop',
};
@Injectable({ providedIn: 'root' })
export class SeoService {
// Other SEO logic...
private readonly _metaService = inject(Meta);
setSeoData(seoData: SeoData): void {
const title = seoData.title ? `${seoData.title} | SSRmart` : 'SSRmart';
// Other SEO logic...
this._updateMetaTag('og:locale', 'en_US');
this._updateMetaTag('og:site_name', 'SSRmart');
this._updateMetaTag('og:title', title);
this._updateMetaTag('og:description', seoData.description);
this._updateMetaTag(
'og:image',
this._getImageParamsUrl(seoData.ogImage?.url, OG_IMAGE_PARAMS)
);
this._updateMetaTag('og:image:width', '1200');
this._updateMetaTag('og:image:height', '630');
this._updateMetaTag('og:image:type', 'image/jpeg');
this._updateMetaTag('og:image:alt', seoData.ogImage?.alt);
this._updateMetaTag('og:url', seoData.ogUrl);
this._updateMetaTag('og:type', seoData.ogType);
// The Twitter @username the card should be attributed to.
this._updateMetaTag('twitter:site', '@ssrmart');
const cardType: TwitterCardType =
seoData.twitterCardType ?? 'summary_large_image';
this._updateMetaTag('twitter:card', cardType);
this._updateMetaTag('twitter:title', title);
this._updateMetaTag('twitter:description', seoData.description);
this._updateMetaTag(
'twitter:image',
this._getImageParamsUrl(seoData.ogImage?.url, TWITTER_IMAGE_PARAMS)
);
this._updateMetaTag('twitter:image:alt', seoData.ogImage?.alt);
this._updateMetaTag('twitter:label1', seoData.twitterLabel?.key);
this._updateMetaTag('twitter:data1', seoData.twitterLabel?.value);
this._updateMetaTag('twitter:label2', seoData.twitterLabel2?.key);
this._updateMetaTag('twitter:data2', seoData.twitterLabel2?.value);
// Other SEO logic...
}
private _updateMetaTag(name: string, content?: string): void {
if (!content) {
this._metaService.removeTag(`property="${name}"`);
return;
}
if (this._metaService.getTag(`property="${name}"`)) {
this._metaService.updateTag({ property: name, content });
} else {
this._metaService.addTag({ property: name, content });
}
}
private _getImageParamsUrl(
imageUrl: string | undefined,
imageParams: ImageParams
): string | undefined {
if (!imageUrl) return undefined;
const url = new URL(imageUrl);
url.search = '';
url.searchParams.set('w', imageParams.width.toString());
url.searchParams.set('h', imageParams.height.toString());
url.searchParams.set('fm', imageParams.format);
url.searchParams.set('fit', imageParams.fit);
return url.toString();
}
}
Aby wesprzeć Twitter Cards, stworzyłem nową stałą TWITTER_IMAGE_PARAMS, która obsługuje konfigurację obrazu Unsplash dla unikalnych proporcji 2:1 wymaganych przez Twittera.
Zaktualizowałem również metodę setSeoData, aby ustawiała wszystkie niezbędne tagi Twittera. Była to prosta zmiana, ponieważ mogliśmy ponownie wykorzystać większość informacji z naszego istniejącego modelu danych konfiguracji SEO.
Zaktualizuj konfigurację SEO strony
Na koniec zaktualizujmy obiekt konfiguracji SEO naszej strony o dane dla Twittera.
import { inject } from '@angular/core';
import { SeoData } from '@ssrmart/client/utils';
import { ConfigService } from '@ssrmart/shared/config';
export const getHomePageSeo = (): SeoData => {
const baseUrl = inject(ConfigService).get('baseUrl');
return {
title: 'Welcome to SSRmart - Your Online Shopping Destination',
description:
'Discover amazing products at great prices. Shop the latest trends in electronics. Fast shipping and excellent customer service.',
ogType: 'website',
ogUrl: baseUrl,
ogImage: {
url: 'https://images.unsplash.com/photo-1498049794561-7780e7231661',
alt: 'Desk with laptop, headphones, smartphone, and smartwatch',
},
twitterCardType: 'summary_large_image',
twitterLabel: {
key: 'Contact Us',
value: 'support@ssrmart.com',
},
twitterLabel2: {
key: 'Follow Us',
value: 'https://www.facebook.com/ssrmart',
},
};
};
Teraz nasza strona główna będzie wyświetlać etykiety Contact Us i Follow Us, gdy nasza witryna zostanie udostępniona na Slacku.
Dla większości stron internetowych standardowe tagi OG i Twittera, które omówiliśmy, to wszystko, czego potrzebujesz. Jednak protokół Open Graph oferuje potężne, specyficzne „typy” dla określonych rodzajów treści.
Zaawansowane metadane OG dla płatności, artykułów, muzyki i nie tylko
Jeśli Twoja strona nie jest tylko ogólną witryną, ale jest artykułem, książką lub filmem/wideo, możesz odblokować zupełnie nowy zestaw właściwości metadanych, które możesz zdefiniować, aby wzmocnić SEO. Pozwala to na dostarczenie znacznie bogatszych, bardziej szczegółowych informacji platformom społecznościowym i wyszukiwarkom.
Na przykład, jeśli ustawisz og:type na article, możesz następnie określić jego dokładny czas publikacji, autora i inne:
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2025-09-16T17:34:00" />
<meta property="article:author" content="https://angular.love/author/dawid" />
Oto szybka tabela referencyjna dla najczęstszych zaawansowanych typów i specjalnych właściwości, które odblokowują. Pełną dokumentację znajdziesz na https://ogp.me/#optional.
| Właściwość najwyższego poziomu | Właściwości metadanych |
| article | published_time, modified_time, expiration_time, author, section, tag |
| payment | description, currency, amount, expires_at, status, id, success_url |
| profile | first_name. last_name, username, gender |
| book | author, isbn, release_date, tag |
| music | song, album, playlist |
| video.movie | actor, actor:role, director, writer, duration, release-date, tag |
| video.episode | actor, actor:role, director, writer, duration, release_date, tag, series |
Implementacja każdego scenariusza byłaby zbyt obszerna, więc skupmy się na jednym, powszechnym przypadku: stronie artykułu. To idealny przykład, aby pokazać, jak dodać opcjonalne metadane OG. W tym przykładzie użyjemy również efektu, aby ustawić tagi artykułu dynamicznie, w oparciu o odpowiedź otrzymaną z API.
Implementacja specyficznych metadanych OG w Angularze
Przejdźmy przez praktyczny przykład implementacji zaawansowanych tagów OG dla strony artykułu.
Zdefiniuj model danych artykułu
Najpierw zdefiniujmy nasz model danych. W tym przypadku ponownie wykorzystam mój istniejący model Article z API, ponieważ zawiera on już wszystkie potrzebne właściwości, takie jak publishedAt, author i tags.
article.model.ts
export type ArticleImage = {
url: string;
alt: string;
};
export type Article = {
id: string;
title: string;
excerpt: string;
content: string;
image: ArticleImage;
author: string;
publishedAt: string;
modifiedAt?: string;
expirationTime?: string;
category: string;
tags: string[];
readTime: number; // in minutes
};
Rozszerz SeoService o obsługę artykułów
Następnie otwieramy seo.service.ts i dodajemy nową metodę publiczną, setArticleMetadata, do obsługi tych nowych, specyficznych dla artykułu właściwości.
seo.service.ts
import { inject, Injectable } from '@angular/core';
import { Meta } from '@angular/platform-browser';
import { Article } from '@ssrmart/shared/types';
// Image params configuration
@Injectable({ providedIn: 'root' })
export class SeoService {
private readonly _metaService = inject(Meta);
// Other SEO logic
setArticleMetadata(article: Article): void {
this._updateMetaTag('article:published_time', article.publishedAt);
if (article.modifiedAt) {
this._updateMetaTag('article:modified_time', article.modifiedAt);
}
if (article.expirationTime) {
this._updateMetaTag('article:expiration_time', article.expirationTime);
}
this._updateMetaTag('article:author', article.author);
this._updateMetaTag('article:section', article.category);
// Set article tags
article.tags.forEach((tag) => {
this._updateMetaTag('article:tag', tag);
});
}
private _updateMetaTag(name: string, content?: string): void {
if (!content) {
this._metaService.removeTag(`property="${name}"`);
return;
}
if (this._metaService.getTag(`property="${name}"`)) {
this._metaService.updateTag({ property: name, content });
} else {
this._metaService.addTag({ property: name, content });
}
}
}
Pobieranie danych za pomocą resolvera routingu
Stwórzmy również resolver routingu do pobierania danych artykułu.
article.resolver.ts`
export const articleResolver: ResolveFn<Article> = (
route: ActivatedRouteSnapshot
) => {
const router = inject(Router);
return inject(ArticleService)
.getArticle(route.params['id'])
.pipe(
catchError(() => of(new RedirectCommand(router.createUrlTree(['/blog']))))
);
};
Po utworzeniu resolvera, dodajmy go do bloku resolve w konfiguracji naszej trasy dla strony artykułu.
routes.ts
import { Routes } from '@angular/router';
import {
articleResolver,
articleSeoResolver,
} from '@ssrmart/client/data-access';
export const ROUTES: Routes = [
// Other routes
{
path: ':id',
loadComponent: () =>
import('@ssrmart/client/feature-article-page').then(
(m) => m.ArticlePageComponent
),
resolve: {
article: articleResolver,
seo: articleSeoResolver,
},
},
];
Aplikowanie tagów w komponencie
Na koniec, w ArticlePageComponent, wstrzykujemy SeoService i używamy efektu, aby przekazać dane artykułu do naszej nowej metody setArticleMetadata.
article-page.ts
import { DatePipe, NgOptimizedImage } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
effect,
inject,
input,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatChipsModule } from '@angular/material/chips';
import { RouterLink } from '@angular/router';
import { ImageSizePipe, SeoService } from '@ssrmart/client/utils';
import { Article } from '@ssrmart/shared/types';
@Component({
selector: 'ssrmart-article-page',
imports: [
NgOptimizedImage,
DatePipe,
ImageSizePipe,
MatButtonModule,
MatCardModule,
MatChipsModule,
RouterLink,
],
templateUrl: './article-page.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArticlePageComponent {
private readonly _seoService = inject(SeoService);
readonly article = input<Article>(); // resolver binding
constructor() {
effect(() => {
const article = this.article();
if (article) {
this._seoService.setArticleMetadata(article);
}
});
}
}
I w ten właśnie sposób zaimplementowaliśmy specyficzne tagi OG dla naszej strony artykułu! Jeśli sprawdzimy odpowiedź HTML, zobaczymy teraz wyrenderowane nowe tagi article:*:
<meta property="article:published_time" content="2024-01-15T10:00:00Z" />
<meta property="article:modified_time" content="2024-01-15T10:00:00Z" />
<meta property="article:author" content="Sarah Johnson" />
<meta property="article:section" content="Audio" />
<meta property="article:tag" content="2024" />
Jako uwaga końcowa do implementacji: warto byłoby zrefaktoryzować logikę SEO związaną z artykułami do oddzielnego serwisu ArticleSeoService. Utrzymałoby to główny SeoService w czystości i uczyniło kod bardziej „tree-shakable” na potrzeby zoptymalizowanej kompilacji produkcyjnej.
Podsumowanie
Dodanie tagów OG i Twitter Cards to potężny sposób na poprawę obecności Twojej aplikacji w mediach społecznościowych. Chociaż ich konfiguracja wymaga pewnego wysiłku (szczególnie przy agregowaniu wszystkich dynamicznych danych z API), jest to warte zachodu. Twoja aplikacja zyska na tym dzięki bogatszym, bardziej profesjonalnie wyglądającym linkom, które mogą znacznie zwiększyć współczynniki klikalności (CTR) na platformach społecznościowych i w komunikatorach.
Mam nadzieję, że ten przewodnik oszczędził Ci godzin poszukiwań i dał jasną ścieżkę do działania. Jeśli szukasz inspiracji, co umieścić w swoich tagach og:image, zobacz Open Graph Examples, ponieważ mają fantastyczną galerię różnych przykładów.
Jeśli masz jakieś pytania, śmiało zadawaj je w komentarzach poniżej!
Oto link do repozytorium GitHub i implementacji SeoService.
