JS SDK
Cea mai rapidă integrare: două linii de HTML, fără build, fără CMS. SDK-ul randează blogul într-un container pe pagina ta existentă și gestionează navigarea client-side.
<div data-baas-blog
data-key="pk_YOUR_KEY"
data-base-path="/blog"
data-api="https://api.dailysmith.com"></div>
<script src="https://api.dailysmith.com/v1/sdk/blog.js" async></script>
Pune asta pe pagina servită la calea de bază a blogului tău (/blog implicit).
Header-ul, footer-ul și stilurile tale rămân neschimbate — blogul se randează în
interiorul containerului și moștenește tipografia site-ului tău.
Atribute
| Atribut | Obligatoriu | Sens |
|---|---|---|
data-key | da | Cheia publică a site-ului tău (pk_…, afișată pe pagina site-ului) |
data-api | da | Originea API-ului de unde se încarcă conținutul — setează-l mereu; fragmentul de mai sus îl fixează la https://api.dailysmith.com |
data-base-path | nu | Calea pe care e montat blogul; implicit /blog. Trebuie să corespundă cu calea de bază a blogului configurată în dashboard și cu calea URL sub care e servită această pagină. |
data-baas-head | nu | Setează la "false" ca să ții SDK-ul complet în afara <head>-ului tău — vezi „Dacă site-ul tău își gestionează propriul head” mai jos. |
data-baas-theme | nu | "light" sau "dark" fixează blogul pe o singură schemă de culoare, indiferent ce face pagina din jur; "inherit" retrage paleta blogului, ca propriul tău CSS să furnizeze fiecare token. Nesetat, blogul urmează data-theme de pe site-ul tău, apoi preferința de sistem a cititorului. Vezi Teme. |
SDK-ul folosește primul element [data-baas-blog] de pe pagină; un singur
container de blog per pagină. Expune și window.baasBlog, cu init(el?) și
destroy(), pentru aplicații cu rutare client-side — vezi mai jos.
Servește shell-ul pentru fiecare URL de blog
Cum faci asta pe configurațiile obișnuite:
Netlify — _redirects:
/blog/* /blog/index.html 200
Vercel — vercel.json:
{ "rewrites": [{ "source": "/blog/:path*", "destination": "/blog" }] }
nginx:
location /blog {
try_files $uri /blog/index.html;
}
Framework-uri — folosește o rută catch-all ca pagină de blog și pune
fragmentul în ea: app/blog/[[...slug]]/page.tsx (Next.js),
src/routes/blog/[...rest]/+page.svelte (SvelteKit),
src/pages/blog/[...slug].astro (Astro).
Compromisul: acum orice URL de blog răspunde cu 200
Rewrite-ul de shell înseamnă că /blog/orice-cale returnează HTTP 200 cu
shell-ul — statusul real se află la un hop distanță, în API. Iată ce se
întâmplă în această zonă de soft-404:
- Când API-ul returnează 404 pentru un articol lipsă, SDK-ul randează o
stare distinctă de „nu a fost găsit”, setează
<meta name="robots" content="noindex">, golește canonical-ul și JSON-LD-ul, și declanșează un eveniment anulabilbaas:notfound— apeleazăpreventDefault()pe el ca să randezi propriul tău UI de 404 în loc. - Eșecurile de rețea și erorile de server sunt separate: SDK-ul declanșează
baas:errorcu{status}(0înseamnă că fetch-ul însuși a eșuat) și afișează un mesaj de „temporar indisponibil”. - Dacă ai nevoie ca URL-urile necunoscute să răspundă cu un HTTP 404 real pentru crawlere, exact asta oferă căile de edge proxy și prerender static — statusul API-ului trece direct.
Aplicații cu rutare client-side (React, Vue, SvelteKit, …)
Scriptul se leagă (bind) când se execută, iar re-executarea lui (sau apelarea
window.baasBlog.init()) se re-leagă idempotent: instanța anterioară e
distrusă mai întâi, deci nu se acumulează niciodată listeneri. destroy()
dezleagă listenerul de popstate, golește containerul și restaurează fiecare
nod din <head> pe care SDK-ul l-a creat sau suprascris.
Într-o aplicație care schimbă rutele client-side, conectează-l la ciclul de viață al rutei de blog:
// Exemplu React — aceeași structură funcționează în hook-ul de mount al oricărui framework
useEffect(() => {
if (window.baasBlog) {
window.baasBlog.init(); // SDK deja încărcat: re-leagă la acest container
} else {
const s = document.createElement('script');
s.src = 'https://api.dailysmith.com/v1/sdk/blog.js';
s.async = true;
document.body.appendChild(s); // se execută și se leagă la load
}
return () => window.baasBlog && window.baasBlog.destroy();
}, []);
return <div data-baas-blog data-key="pk_YOUR_KEY"
data-base-path="/blog" data-api="https://api.dailysmith.com" />;
Alternativă: navigare hard (full page load) către blog. Dacă linkurile
tale către blog sunt tag-uri <a> simple care declanșează un page load
complet (scoate ruta de blog din interceptarea de linkuri a routerului tău),
fragmentul static funcționează așa cum e.
Încă o observație despre router: SDK-ul navighează cu history.pushState,
lucru pe care routerele client-side nu îl pot observa. Declanșează
baas:navigate după fiecare push, ca analytics-ul, breadcrumbs-urile sau
managerii de scroll să se poată conecta la el (vezi Evenimente).
Dacă site-ul tău își gestionează propriul head
Sub calea de bază, SDK-ul deține document.title, meta-tagurile de descriere
și og:*, link-ul canonical și JSON-LD-ul. Acesta e contractul — nu le mai
gestiona și din routerul tău propriu cât timp un URL de blog e activ, altfel
cele două sisteme se vor lupta între ele. SDK-ul marchează fiecare nod pe care
îl creează cu un atribut data-baas, reține valoarea originală a oricărui
nod-gazdă pe care îl suprascrie și pune totul înapoi la destroy().
Dacă site-ul tău are propriul strat de SEO, renunță în schimb: setează
data-baas-head="false" pe container și aplică tu metadatele, din evenimentul
baas:page, care poartă tot ce are nevoie head-ul.
Evenimente
Toate evenimentele sunt CustomEvent-uri care urcă (bubble) din container,
deci poți asculta pe document:
| Eveniment | Detaliu | Note |
|---|---|---|
baas:page | {path, kind, title, description, canonical, json_ld, locale, og_locale, hero_image_url} | După fiecare randare reușită (kind e index sau post) |
baas:notfound | {path} | Articol lipsă (API 404). Anulabil — preventDefault() și randează propriul tău 404 |
baas:error | {status, path} | Fetch eșuat; status: 0 înseamnă eroare de rețea. Anulabil |
baas:navigate | {path} | După history.pushState — routerele nu îl pot vedea, acesta e hook-ul tău |
document.addEventListener('baas:notfound', (e) => {
e.preventDefault();
renderMyOwn404();
});
document.addEventListener('baas:navigate', (e) => {
analytics.pageview(e.detail.path);
});
Content-Security-Policy
Un site cu CSP strict are nevoie de originea API-ului în patru directive:
script-src 'self' https://api.dailysmith.com # scriptul SDK-ului propriu-zis
connect-src 'self' https://api.dailysmith.com # fetch-urile de conținut (fragment)
img-src 'self' data: https://api.dailysmith.com # imaginile hero ale articolelor
style-src 'self' https://api.dailysmith.com 'unsafe-inline'
style-src are nevoie de originea API-ului pentru foaia de stil a
conținutului bogat (încărcată ca <link>), iar 'unsafe-inline' doar dacă
blogul tău are o paletă de temă — tema per-blog e injectată
ca un singur element <style> inline, mic.
Ce face SDK-ul
- Preia fragmente HTML randate server-side și le injectează în container
(
aria-busye setat cât timp se încarcă). - Actualizează
document.title, meta descrierea, tagurile Open Graph, link-ul canonical și JSON-LD-ul per pagină — marcate cudata-baas, restaurate ladestroy(). - Interceptează linkurile din interiorul blogului pentru navigare
instantanee prin History API; back/forward funcționează cum te aștepți;
baas:navigatese declanșează pentru routerul sau analytics-ul tău. - Încarcă o singură dată foaia de stil a componentelor de conținut bogat, ca
un
<link>cacheable (#baas-md2-css→https://api.dailysmith.com/v1/sdk/md2.css) și injectează paleta de temă a blogului tău ca un<style>inline mic (#baas-theme-css), înlocuindu-l când se schimbă.
Site-uri statice și hosturi prerandate
Două lucruri contează când site-ul tău e prerandat sau complet static:
- Regula de rewrite de la „Servește shell-ul” trebuie emisă de build-ul
tău (un fișier
_redirectscommit-uit, unvercel.jsonîn repo) — o regulă adăugată manual dintr-un dashboard dispare la următoarea schimbare a pipeline-ului de deploy. - Până rulează JavaScript-ul, fiecare URL de articol servește
<head>-ul shell-ului prerandat — titlul și canonical-ul shell-ului, nu ale articolului. Crawlerele care execută JS primesc valorile corecte; pentru HTML garantat per URL, rulează CLI-ul de prerender la build — output-ul lui dublează și rolul de shell.
Verifică că funcționează
Deschide indexul blogului Vizitează
/blogpe site-ul tău. Lista de articole ar trebui să se randeze în interiorul chrome-ului paginii tale. În tab-ul network din devtools vei vedea o cerere cătrehttps://api.dailysmith.com/v1/render/pk_…/blog?format=fragment— acesta e și verificarea care funcționează pentru instalările SPA, unde script tag-ul e injectat la runtime și nu apare niciodată în HTML-ul servit.Intră într-un articol, apoi dă refresh URL-ul ar trebui să se schimbe în
/blog/<slug>, titlul documentului ar trebui să se actualizeze — iar un hard refresh pe acel URL trebuie să randeze același articol. Dacă refresh-ul dă 404, revizitează „Servește shell-ul pentru fiecare URL de blog” mai sus.Verifică head-ul și starea de 404 După ce deschizi un articol, head-ul documentului ar trebui să poarte titlul, meta descrierea, URL-ul canonical și un tag
#baas-jsonldde script. Apoi vizitează/blog/definitely-not-a-post: ar trebui să vezi starea de „nu a fost găsit”, iar head-ul ar trebui să poarte<meta name="robots" content="noindex">.
SEO: combină-l cu livrare server-side
SDK-ul de unul singur randează după ce se încarcă JavaScript-ul. Crawlerele moderne execută JS, dar pentru SEO garantat la nivel de crawler, servește același conținut și static:
- Edge proxy — o regulă de rewrite servește pagini SSR complete de pe domeniul tău; SDK-ul apoi face upgrade la navigare client-side. (Regula de proxy satisface și cerința de shell de mai sus, iar URL-urile necunoscute returnează 404-uri reale.)
- Prerender static — generează fișiere HTML la deploy; paginile prerandate dublează și rolul de shell pentru fiecare URL de articol.