# HTML na obrazek: twój HTML i CSS jako PNG z prawdziwego Chrome

API „HTML na obrazek” składa twój kod i CSS w przeglądarce i oddaje zdjęcie wyniku. W Shareglint zapisujesz HTML i CSS raz, jako szablon; potem każdy obrazek to jedno żądanie HTTP — POST /v1/images z nazwą szablonu i tekstem, który się zmienia — a w odpowiedzi dostajesz adres pliku PNG, JPEG, WebP albo PDF, zwykle po około sekundzie.

To prawdziwy Chrome, a nie odtworzony CSS: grid, flexbox, gradienty, cienie, emoji i tekst od prawej do lewej wychodzą tak jak w twojej przeglądarce. Wypróbuj to niżej, bez zakładania konta.

## Jak to działa w API

1. Zapisz szablon raz. POST /v1/templates z twoim HTML-em, CSS-em, wymiarami i nazwami pól, które się zmieniają. Pola wstawiasz do HTML-a jako {{ nazwa }}; HTML jest czyszczony przy zapisie.
2. Wyrenderuj obrazek. POST /v1/images ze slugiem szablonu, wartościami pól i formatem. Z ?wait=true adres gotowego pliku wraca na tym samym połączeniu.
3. Użyj adresu. W og:image, w mailu, w feedzie albo do pobrania. Te same pola drugi raz zwracają istniejący plik i nie zużywają limitu.

Podczas projektowania POST /v1/templates/preview renderuje szkic bez zapisywania i nie zużywa limitu.

## Kod: curl, JavaScript, PHP i Python

### curl

```bash
# 1. Save the template once
curl -X POST "https://shareglint.com/v1/templates" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Article card",
  "width": 1200,
  "height": 630,
  "fields": [
    "kicker",
    "title",
    "author"
  ],
  "html": "<div class=\"card\"><p class=\"kicker\">{{ kicker }}</p><div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ title }}</h1></div><p class=\"by\">{{ author }}</p></div>",
  "css": ".card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}"
}'
# → {"id": "tpl_…", "slug": "article-card", …}

# 2. One call per image (use the slug from step 1)
curl -X POST "https://shareglint.com/v1/images?wait=true" \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "template": "article-card",
  "format": "png",
  "modifications": {
    "kicker": "Engineering",
    "title": "How we cut our build time in half",
    "author": "Ada Lovelace"
  }
}'
```

### JavaScript

```js
const API = 'https://shareglint.com/v1';
const headers = {
  Authorization: 'Bearer sk_live_YOUR_KEY',
  'Content-Type': 'application/json',
};

// 1. Save the template once
const template = await fetch(`${API}/templates`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    name: "Article card",
    width: 1200,
    height: 630,
    fields: ['kicker', 'title', 'author'],
    html: "<div class=\"card\"><p class=\"kicker\">{{ kicker }}</p><div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ title }}</h1></div><p class=\"by\">{{ author }}</p></div>",
    css: ".card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}",
  }),
}).then((r) => r.json());

// 2. One call per image
const image = await fetch(`${API}/images?wait=true`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    template: template.slug,
    format: 'png',
    modifications: {
      kicker: "Engineering",
      title: "How we cut our build time in half",
      author: "Ada Lovelace",
    },
  }),
}).then((r) => r.json());

console.log(image.url);
```

### PHP (Laravel)

```php
use Illuminate\Support\Facades\Http;

$api = Http::withToken('sk_live_YOUR_KEY')->baseUrl('https://shareglint.com/v1');

// 1. Save the template once
$template = $api->post('/templates', [
    'name' => 'Article card',
    'width' => 1200,
    'height' => 630,
    'fields' => ['kicker', 'title', 'author'],
    'html' => '<div class="card"><p class="kicker">{{ kicker }}</p><div class="fitbox"><h1 class="fit" data-autofit>{{ title }}</h1></div><p class="by">{{ author }}</p></div>',
    'css' => '.card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}',
])->json();

// 2. One call per image
$image = $api->post('/images?wait=true', [
    'template' => $template['slug'],
    'format' => 'png',
    'modifications' => [
        'kicker' => 'Engineering',
        'title' => 'How we cut our build time in half',
        'author' => 'Ada Lovelace',
    ],
])->json();

echo $image['url'];
```

### Python

```python
import requests

API = "https://shareglint.com/v1"
HEADERS = {"Authorization": "Bearer sk_live_YOUR_KEY"}

# 1. Save the template once
template = requests.post(f"{API}/templates", headers=HEADERS, json={
    "name": "Article card",
    "width": 1200,
    "height": 630,
    "fields": ["kicker", "title", "author"],
    "html": "<div class=\"card\"><p class=\"kicker\">{{ kicker }}</p><div class=\"fitbox\"><h1 class=\"fit\" data-autofit>{{ title }}</h1></div><p class=\"by\">{{ author }}</p></div>",
    "css": ".card{height:100%;padding:72px;display:grid;grid-template-rows:auto 1fr auto;gap:24px;background:#0f172a;color:#f8fafc;font-family:Roboto,sans-serif} .kicker{color:#fbbf24;font-size:26px;letter-spacing:.1em;text-transform:uppercase} h1{font-size:76px;line-height:1.08} .by{font-size:28px;color:#94a3b8}",
}).json()

# 2. One call per image
image = requests.post(f"{API}/images?wait=true", headers=HEADERS, json={
    "template": template["slug"],
    "format": "png",
    "modifications": {
        "kicker": "Engineering",
        "title": "How we cut our build time in half",
        "author": "Ada Lovelace",
    },
}).json()

print(image["url"])
```

### Co wraca

```json
{
    "id": "img_01m08s3ez1zcv28h335g9923bw",
    "kind": "image",
    "status": "completed",
    "format": "png",
    "size": "og",
    "width": 1200,
    "height": 630,
    "url": "https://cdn.shareglint.com/renders/img_01m08s3ez1zcv28h335g9923bw.png",
    "duration_ms": 912
}
```

## Formaty wyjściowe

- `png`: Bezstratny. scale 2 (domyślnie) podwaja piksele: szablon 1200×630 daje plik 2400×1260; scale 1 daje dokładnie 1200×630.
- `jpeg`: Mniejsze pliki przy zdjęciach; jakość od 1 do 100, domyślnie 85.
- `webp`: Jeszcze mniejsze, z tym samym ustawieniem jakości co JPEG.
- `pdf`: Jedna strona dokładnie w rozmiarze szablonu, z tłami, tekst zostaje wektorowy i da się go zaznaczyć.
- `gif`, `mp4`: Animacja: nagrywa animacje CSS szablonu, do 5 sekund; każdy kosztuje 5 renderów z limitu.

Jeden szablon renderuje się też w każdym gotowym rozmiarze (og 1200×630, square 1080×1080, portrait 1080×1350, story 1080×1920, pinterest 1000×1500, youtube 1280×720) — jaki rozmiar chce każda platforma. https://shareglint.com/pl/og-image-size

## Dlaczego prawdziwy Chrome, a nie biblioteka

Są trzy popularne sposoby, żeby samemu zamienić HTML na obrazek. Każdy działa; każdy ma koszt, który wychodzi później.

| Sposób | Jaki CSS renderuje | Co bierzesz na siebie |
|---|---|---|
| Satori / @vercel/og | Podzbiór: JSX z układem flexbox, bez CSS grid | Kartę piszesz w JSX, pliki fontów ładujesz sam, a całość chodzi w twojej funkcji Next.js albo edge |
| wkhtmltoimage | Stary silnik Qt WebKit: bez CSS grid, nowoczesny CSS zawodny | Narzędzie, którego projekt zarchiwizowano w 2023 roku, zainstalowane na twoich serwerach |
| Puppeteer albo Playwright na twoich serwerach | Wszystko, co renderuje Chrome | Pamięć i awarie Chrome, fonty na każdej maszynie, kolejka i izolowanie HTML-a, którego nie napisałeś |
| Shareglint | Wszystko, co renderuje Chrome | Jedno żądanie HTTP; Chrome, fonty, piaskownica, cache i przechowywanie są po naszej stronie |

Jeśli strona stoi na Next.js, a karta mieści się we flexboksie, @vercel/og jest darmowy i działa tuż obok twoich stron — bierz go. Własny Chrome ma sens, gdy już go utrzymujesz na dużą skalę. API opłaca się wtedy, gdy nie chcesz robić ani jednego, ani drugiego.

## Ograniczenia, wprost

- Bez JavaScriptu: skrypty są usuwane przy zapisie szablonu, więc animuj CSS-em. Żywą stronę, która potrzebuje swoich skryptów, zrzuć z jej adresu przez Screenshot API.
- Bez zewnętrznych adresów w HTML-u i CSS-ie. Obrazki wchodzą przez pola obrazków — pobieramy je, sprawdzamy i osadzamy — albo jako adresy data:.
- Fonty z naszej listy (Roboto, Open Sans, Carlito, DejaVu Sans, DejaVu Serif, DejaVu Sans Mono, Ubuntu, Noto Sans CJK JP, Noto Sans CJK SC, Noto Color Emoji); własnych plików fontów nie wgrasz.
- HTML i CSS do 64 KB każdy, od 200 do 4,000 pikseli na bok.

## Ile to kosztuje

Free: 100 obrazków miesięcznie, bez karty, z małym znakiem wodnym. Starter: $9 miesięcznie za 2,000 obrazków ($4.50 za tysiąc), bez znaku wodnego. Render, który już masz — ten sam szablon, te same pola, ten sam format — wraca z cache i nie zużywa limitu.

## Pytania

### Czym jest API „HTML na obrazek”?

Usługą sieciową, która bierze HTML i CSS, składa je w silniku przeglądarki i zwraca obraz wyniku — PNG, JPEG, WebP albo PDF. Przydaje się, gdy obrazek ma powstawać z danych: karty do mediów społecznościowych, certyfikaty, rachunki, wykresy z podpisami, banery do maili.

### Jak zamienić HTML na PNG przez API?

W Shareglint: zapisz HTML i CSS raz przez POST /v1/templates, potem wywołaj POST /v1/images?wait=true ze slugiem szablonu, wartościami pól i "format": "png". Odpowiedź zawiera adres PNG.

### Czy mogę zamienić HTML na PDF?

Tak. "format": "pdf" zwraca jedną stronę dokładnie w rozmiarze szablonu, z wydrukowanymi tłami i tekstem w wektorze, więc zostaje ostry i da się go zaznaczyć.

### Czy działa CSS grid, flexbox i własne fonty?

Grid, flexbox i reszta nowoczesnego CSS-a działają, bo rendererem jest Chrome. Fonty pochodzą z rodzin zainstalowanych w rendererze (Roboto, Open Sans, Carlito, DejaVu Sans, DejaVu Serif, DejaVu Sans Mono, Ubuntu, Noto Sans CJK JP, Noto Sans CJK SC, Noto Color Emoji); wgrywania własnych plików fontów nie ma.

### Czy HTML może uruchamiać JavaScript?

Nie. Szablony są czyszczone przy zapisie i skrypty znikają — dzięki temu można bezpiecznie renderować HTML od kogokolwiek. Stronę, która potrzebuje swoich skryptów, zrzucisz przez Screenshot API z jej adresu.

### Czy jest darmowe API do zamiany HTML na obrazek?

Plan Free renderuje 100 obrazków miesięcznie bez karty, z małym znakiem wodnym. Podgląd na tej stronie nie wymaga nawet konta.

### Czym to się różni od HTMLCSStoImage?

HTMLCSStoImage przyjmuje cały HTML w każdym żądaniu. Shareglint przechowuje szablon raz i przyjmuje tylko zmieniające się pola, dopasowuje długość tekstu, ma cache, który nie zużywa limitu, i 153 gotowych szablonów. Jeśli twój HTML jest za każdym razem inny, ich model pasuje lepiej.

## Powiązane

- [Własne szablony w dokumentacji](https://shareglint.com/pl/docs#szablony)
- [Opis API: POST /v1/templates](https://shareglint.com/pl/docs/api#post-templates)
- [Gotowe szablony obrazków OG](https://shareglint.com/pl/templates)
- [Generator obrazków OG dla twojego frameworka](https://shareglint.com/pl/og-image-generator)
- [Screenshot API (render adresu URL)](https://shareglint.com/pl/screenshot-api)
- [Shareglint a HTMLCSStoImage](https://shareglint.com/pl/alternatives/htmlcsstoimage)
- [Rozmiar obrazka OG na każdej platformie](https://shareglint.com/pl/og-image-size)
