Jaspr + Dart ile Modern Web Geliştirme: Sıfırdan İleri Seviyeye Rehber
Jaspr, tamamen Dart ile yazılmış, Flutter Web gibi canvas'a çizmek yerine gerçek HTML/DOM/CSS üreten full-stack bir web framework'üdür. Bu rehber kurulumdan deployment'a, component sisteminden state yönetimine, SSR/SSG/client rendering modlarından test yazımına kadar her şeyi kapsar.

Özet: Jaspr, tamamen Dart ile yazılmış full-stack bir web framework'üdür. Flutter Web gibi canvas'a piksel çizmek yerine gerçek HTML, DOM ve CSS üretir — bu da onu SEO dostu, ilk boyamada hızlı ve web ekosistemiyle tam uyumlu kılar. Flutter'a benzer bir component modeli (StatelessComponent, StatefulComponent, InheritedComponent) ve üç rendering modu (static/SSG, server/SSR, client/SPA) sunar. Bu rehber kurulumdan başlayıp component sistemi, routing, state yönetimi, rendering modları, stil, API entegrasyonu, kimlik doğrulama, deployment ve test konularını kapsar.
Jaspr nedir ve neden var?
Jaspr, tamamen Dart ile yazılmış modern, full-stack bir web framework'üdür. Flutter'a görünüş ve his olarak çok benzeyen bir component modeli sunar; ancak Flutter Web'in aksine canvas'a piksel çizmek yerine gerçek HTML, DOM ve CSS üretir. Yazdığınız Dart kodu, tarayıcının doğal olarak anladığı standart web teknolojilerine dönüşür.
Jaspr üç temel soruya şöyle cevap verir:
- Neden? Flutter gibi görünen ve hissettiren, fakat normal HTML ve CSS render eden bir web framework'ü oluşturmak için.
- Kimin için? Dart ile her tür web sitesi (özellikle Flutter Web'e uygun olmayanlar) geliştirmek isteyen, ağırlıklı olarak Flutter geliştiricileri için.
- Ne? Dart'ın web ve sunucudaki sınırlarını zorlayan, baştan sona düşünülmüş bir full-stack web framework'ü.
Öne çıkan özellikler:
- Tanıdık: Flutter widget'larına benzer component modeli (
StatelessComponent,StatefulComponent,InheritedComponent). - Güçlü: Server-side rendering (SSR) kutudan çıkar çıkmaz hazır.
- Kolay: Sunucu ve istemci arasında component state'ini otomatik senkronize eder (
@sync). - Hızlı: Sadece gereken yerlerde optimize DOM güncellemeleri yapar.
- Esnek: Sunucuda, istemcide veya her ikisinde birden çalışabilir.
İlginç bir kanıt: dart.dev ve docs.flutter.dev sitelerinin ikisi de Jaspr ile geliştirilmiştir. Bu, Jaspr'ın metin ağırlıklı, içerik odaklı sitelerde ne kadar yetkin olduğunun iyi bir göstergesidir.
Jaspr'ın Dart ve Flutter ile ilişkisi nedir?
Jaspr, Flutter'ın alternatifi değil, tamamlayıcısıdır:
- Dart dildir; hem Flutter hem Jaspr bu dille yazılır.
- Flutter, mobil/masaüstü/web için canvas tabanlı bir UI toolkit'idir. Web'de Skia/CanvasKit ile piksel çizer.
- Jaspr, Flutter'ın component modelini ödünç alır ama render katmanı tamamen web'e özgüdür: HTML etiketleri, DOM ağacı ve CSS.
Flutter ekibi kendi dokümantasyonunda açıkça belirtir: "Flutter Web, web siteleri değil web uygulamaları içindir." Metin ağırlıklı, akış tabanlı, statik içerikler (blog yazıları gibi) web'in doküman merkezli modelinden faydalanır. Jaspr tam olarak bu boşluğu doldurur.
Jaspr hangi problemleri çözer?
- SEO problemi: Flutter Web, içeriği canvas içine çizdiği için arama motorları içeriği göremez. Jaspr, sunucuda gerçek HTML ürettiği için SEO dostu siteler sağlar.
- İlk yükleme hızı: Flutter Web'de büyük bir JavaScript bundle'ı yüklenmeden ekran boş kalır. Jaspr'da pre-render edilmiş HTML anında görüntülenir (çok hızlı First Contentful Paint).
- Web standartlarıyla uyum: Metin seçimi, tarayıcı eklentileri, erişilebilirlik araçları ve mevcut CSS ekosistemi gerçek DOM kullanıldığı için doğal olarak çalışır.
- Full-stack tutarlılık: Frontend ve backend'i aynı dilde (Dart) yazarsınız; modelleri, validasyon kodunu ve iş mantığını iki taraf arasında paylaşabilirsiniz.
Hangi projelerde Jaspr tercih edilmeli?
| Proje Tipi | Uygunluk | Önerilen Mod |
|---|---|---|
| Blog, dokümantasyon, tanıtım, portföy | Çok uygun | static |
| Kurumsal site, SEO kritik sayfalar | Çok uygun | static / server |
| Kullanıcı girişli dinamik web uygulaması | Uygun | server |
| Dashboard, admin paneli, SPA | Uygun | client |
| Ağır grafik/animasyon, oyun benzeri UI | Uygun değil | Flutter Web düşün |
| Mobil uygulama | Uygun değil | Flutter kullan |
Jaspr ile Flutter Web arasındaki fark nedir?
| Özellik | Jaspr | Flutter Web |
|---|---|---|
| Render hedefi | Gerçek HTML/DOM + CSS | Canvas (piksel çizimi) |
| SEO | Mükemmel (pre-render edilmiş HTML) | Zayıf |
| Layout | HTML elementleri + CSS (flexbox, grid) | Widget'lar (Row, Column, Stack) |
| Stil | CSS / Styles sınıfı / CSS framework'leri | Widget parametreleri, tema |
| Hazır component | Yok (HTML etiketleri var) | Zengin Material/Cupertino seti |
| İlk yükleme | Hızlı (küçük JS, pre-render) | Yavaş (büyük bundle) |
| İdeal kullanım | Web siteleri, içerik, SEO'lu uygulamalar | Uygulama benzeri web arayüzleri |
Dikkat çekici olarak Jaspr, Flutter'ın Row, Column, Stack gibi layout primitive'lerini bilerek kopyalamaz. Bunun yerine div gibi standart HTML elementlerini CSS ile kullanırsınız. Amaç, web platformunun kendi güçlü yanlarını gizlememek ve geliştiriciyi web'e özgü teknolojilere yakın tutmaktır.
Jaspr nasıl kurulur ve proje nasıl oluşturulur?
Gereksinimler: Dart SDK 3.10+ (dot-shorthand yazımı ve güncel Jaspr için önerilir), bir metin editörü (VS Code + Jaspr Extension önerilir) ve terminal erişimi. Flutter SDK yalnızca Flutter embedding veya plugin'leri kullanacaksanız gerekir.
CLI kurulumu (güncel, önerilen AOT derlenmiş yöntem):
# Güncel (önerilen, daha hızlı AOT executable):
dart install jaspr_cli
# Eski (hâlâ çalışır):
dart pub global activate jaspr_cliProje oluşturma ve çalıştırma:
jaspr create my_website
cd my_website
jaspr serve # http://localhost:8080 adresinde hot reload'lu geliştirme sunucusuProje şablonları rendering modlarına karşılık gelir:
| Şablon | Mod | Ne zaman? |
|---|---|---|
| Static Site | static | Genel amaçlı (önerilen başlangıç): blog, dokümantasyon |
| Server Rendered Site | server | Backend entegrasyonu, auth, sık değişen içerik |
| Single Page Application | client | Uygulama benzeri siteler: dashboard, admin paneli |
| Embedded Flutter Site | static + Flutter | Flutter widget'ları gömülü siteler |
| Custom Backend Site | server + shelf | Kendi API'nizi yazacağınız full-stack projeler |
Component sistemi nasıl çalışır?
Jaspr'da UI'ın her parçası bir Component'tir. Üç tip vardır: StatelessComponent, StatefulComponent ve InheritedComponent — yapısal olarak Flutter'ın widget'larıyla neredeyse birebir aynıdır.
Durumsuz bir component:
import 'package:jaspr/jaspr.dart';
class SelamComponent extends StatelessComponent {
const SelamComponent({super.key});
Component build(BuildContext context) {
return p([.text('Merhaba Dünya')]);
}
}setState ile durumlu bir component:
class Sayac extends StatefulComponent {
const Sayac({super.key});
State<Sayac> createState() => SayacState();
}
class SayacState extends State<Sayac> {
int sayi = 0;
Component build(BuildContext context) {
return div([
p([.text('Sayaç: $sayi')]),
button(
onClick: () => setState(() => sayi++),
[.text('Artır')],
),
]);
}
}Her yaygın HTML etiketi bir component fonksiyonu olarak hazır gelir. Temel kurallar: çocuk listesini en sona koyun (div(id: 'main', [.text('Merhaba')])) ve düz metni her zaman .text('...') ile sarın — div(['Merhaba']) derlenmez.
Her HTML component'inin aldığı ortak parametreler: id, classes, styles, attributes, events ve key.
Jaspr'da routing nasıl çalışır?
Routing için jaspr_router paketi kullanılır; API'si Flutter'daki go_router'a çok benzer:
Router(routes: [
Route(path: '/', builder: (context, state) => Home()),
Route(path: '/about', builder: (context, state) => About()),
Route(path: '/users/:userId',
builder: (context, state) => UserScreen(id: state.params['userId']!)),
])İki routing stratejisinden birini seçersiniz:
- Multi-page (sunucu): Gerçek sayfa yüklemesi; navigasyon
Link/<a>ile. Geleneksel siteler, bloglar, kurumsal siteler için idealdir. - Single-page (istemci): Tamamen istemcide; navigasyon
context.push(...)ile. Uygulama benzeri siteler ve dashboard'lar için idealdir.
Ortak yerleşim (navigasyon çubuğu gibi) için ShellRoute, kod bölme için deferred import'lu Route.lazy, özel 404 sayfası için errorBuilder kullanın. Korumalı sayfaları redirect ile koruyun:
Router(
redirect: (context, state) {
if (!AuthState.of(context).isSignedIn) return '/giris';
return null;
},
routes: [ /* ... */ ],
)Jaspr'da state nasıl yönetilir?
Jaspr'ın state modeli Flutter ile aynıdır: component ağacı, setState() ile tetiklenen yeniden build'ler ve veriyi ağaçta aşağı taşıyan InheritedComponent'ler. Fark, render hedefinin DOM olmasıdır — setState çağrıldığında Jaspr yalnızca değişen DOM düğümlerini günceller.
Yerel durum için StatefulComponent + setState yeterlidir. Global state için önerilen yaklaşım jaspr_riverpod'dir:
final sayacProvider = StateProvider<int>((ref) => 0);
class SayacSayfasi extends StatelessComponent {
const SayacSayfasi({super.key});
Component build(BuildContext context) {
final sayi = context.watch(sayacProvider);
return div([
p([.text('Sayaç: $sayi')]),
button(
onClick: () => context.read(sayacProvider.notifier).state++,
[.text('Artır')],
),
]);
}
}Sunucu → istemci state senkronizasyonu
Static/server modunda sayfa sunucuda render edilir, ardından istemcide hydrate edilir. @sync anotasyonu bir State alanını sunucu pre-render sırasında serialize eder ve istemcide initState() sırasında geri yükler. @client component'inin constructor parametreleri de sunucudan istemciye otomatik serialize edilir — veri aktarmanın en pratik yoludur. Desteklenen parametre tipleri bool, int, double, String ve bunların List/Map'leridir; özel tipler için @encoder/@decoder kullanın.
Üç rendering modu (SSR, SSG, client) nedir?
| static | server | client | |
|---|---|---|---|
| HTML/CSS üretimi | Evet | Evet | Hayır |
| Pre-rendering | Build zamanında | Her istekte | Yok |
| İstemci etkileşimi | Evet | Evet | Evet |
| Sunucu gerektirir | Hayır | Evet | Hayır |
| Statik hosting'e deploy | Evet | Hayır | Evet |
| Özel backend | Hayır | Evet | Hayır |
- SSR (server): Her istek için sunucuda tam HTML üretir, sonra hydrate eder. Girişe özel içerik ve sık değişen veriler için doğru seçimdir.
- SSG (static):
jaspr buildtüm route'ları build zamanında statik.htmldosyalarına render eder. Sunucu gerekmez. - Client: Her şey tarayıcıda çalışır. SEO'nun gereksiz olduğu dashboard'lar için uygundur.
Hedefli hydration önemli bir optimizasyondur: yalnızca @client işaretli component'ler hydrate edilir, bu yüzden @client'i ağaçta mümkün olduğunca aşağı taşıyın (örn. tüm sayfa yerine tek bir "Beğen" butonu).
Scope önemlidir: sunucu scope'undaki component'ler dart:io kullanabilir ama dart:js_interop/package:web kullanamaz; istemci scope'unda tam tersidir. İki ortamda da çalışmak için package:universal_web, hataları erken yakalamak için jaspr_lints'in unsafe_imports kuralını kullanın.
Jaspr uygulamaları nasıl stillendirilir?
Jaspr stil konusunda kasıtlı olarak esnektir: tip-güvenli CSS-in-Dart sistemi, harici stil dosyaları ve CSS framework'leri birlikte kullanılabilir.
Önerilen yaklaşım component seviyesinde @css stilidir:
class App extends StatelessComponent {
const App({super.key});
Component build(BuildContext context) {
return div(classes: 'main', [
p([.text('Merhaba Dünya')]),
]);
}
static List<StyleRule> get styles => [
css('.main', [
css('&').styles(width: 100.px, padding: Padding.all(10.rem)),
css('p').styles(color: Colors.blue),
]),
];
}Flutter'daki Row/Column yerine CSS flexbox ve grid kullanırsınız. Responsive tasarım css.media(...) ile yapılır. Tailwind (jaspr_tailwind), Bulma ve Sass (sass_builder) entegre olur — Jaspr siteleri sıradan web siteleridir, dolayısıyla CSS ekosistemindeki neredeyse her şeyi kullanabilirsiniz.
API ve backend entegrasyonu nasıl yapılır?
İstemcide package:http (tarayıcıda fetch tabanlı) kullanılır. Üç durum deseni (loading / error / data) her veri yükleme ekranında karşınıza çıkar; bunu alışkanlık haline getirin.
Sunucuda (static/server modu) AsyncBuilder ve AsyncStatelessComponent build metodunda await kullanmanızı sağlar (yalnızca sunucu), PreloadStateMixin ise initState() öncesi asenkron veri yükler. Tipik blog senaryosu için @sync + PreloadStateMixin birleştirin: sunucuda yükleyin, istemcide filtreleyin.
Özel backend için serveApp(handler), Jaspr'ın sunucu tarafını tek bir shelf handler'a dönüştürür; böylece shelf, Serverpod veya dart_frog ile birleştirebilirsiniz. Sunucu tarafı context API'leri: context.url, context.headers, context.cookies, context.setHeader, context.setCookie ve context.setStatusCode.
Kimlik doğrulama nasıl yapılır?
Hazır bir auth modülü yoktur; Jaspr'ın yapı taşlarını (cookie'ler, redirect'ler, sunucu context'i) standart web kalıplarıyla birleştirirsiniz. Bu mimari server modu gerektirir. Klasik ve güvenli yaklaşım oturum çerezi (session cookie) kullanmaktır:
- Kullanıcı login formunu gönderir → sunucu kimliği doğrular.
- Sunucu,
HttpOnlybir cookie ile oturum kimliği yazar. - Sonraki her istekte sunucu, SSR sırasında cookie'yi okur.
- Korumalı sayfalar Router
redirect'i ile korunur.
Güvenlik esasları: şifreleri asla düz metin saklamayın (bcrypt/argon2 ile hash'leyin); cookie'leri HttpOnly + Secure + SameSite ile ayarlayın; auth'u her zaman sunucuda doğrulayın — istemci kontrolleri yalnızca kullanıcı deneyimi içindir.
Jaspr uygulaması nasıl deploy edilir?
jaspr build çıktıyı build/jaspr klasörüne yazar. Static/client çıktısı tamamen statik dosyalardır (herhangi bir statik hosting'e yüklenir); server çıktısı bir app çalıştırılabilir dosyası artı bir web/ asset klasörüdür.
- Statik hosting: Firebase Hosting, GitHub Pages (GitHub Actions ile). Repo
<kullanici>.github.iodeğilse base URL'e dikkat edin. - Server modu: Docker (Cloud Run, fly.io, DigitalOcean, AWS) — resmi Dockerfile
dart:stableile build alıpscratch'ten çalıştırır. - Environment variables: Sunucuda
Platform.environmentile okuyun; gizli anahtarları asla istemci koduna koymayın — istemciye derlenen her şey herkes tarafından görülebilir.
Jaspr uygulamaları nasıl test edilir?
jaspr_test paketi package:test üzerine kuruludur ve API'si flutter_test'e benzer. Üç test fonksiyonu tüm yığını kapsar:
| Fonksiyon | Ortam | Kullanım |
|---|---|---|
testComponents | Simüle edilmiş | Component birim testleri |
testClient | Headless tarayıcı | DOM etkileşimleri |
testServer | Sanal HTTP sunucusu | SSR istek/yanıt testleri |
Bir component testi örneği:
import 'package:jaspr_test/jaspr_test.dart';
import '../lib/components/sayac.dart';
void main() {
group('Sayac component testi', () {
testComponents('sayac artirilabilmeli', (tester) async {
tester.pumpComponent(Sayac());
expect(find.text('Sayaç: 0'), findsOneComponent);
await tester.click(find.tag('button'));
expect(find.text('Sayaç: 1'), findsOneComponent);
});
});
}Test stratejisi: saf mantık (validators, servisler, modeller) package:test ile; component'ler testComponents ile; SSR davranışı testServer ile; kritik akışlar testClient ile.
SSS
Jaspr, Flutter'ın yerini mi alıyor?
Hayır. Jaspr, Flutter'ı tamamlar. Flutter, uygulama benzeri arayüzler ve mobil için ideal olan canvas tabanlı bir toolkit'tir; Jaspr gerçek HTML/CSS render eder ve web siteleri, içerik ve SEO kritik uygulamalar için idealdir. İkisi de Dart ile yazılır ve Jaspr, Flutter'ın component modelini ödünç alır.
Jaspr öğrenmek için Flutter bilmem gerekir mi?
Şiddetle önerilir ama kesinlikle zorunlu değildir. Flutter'ın StatelessWidget, StatefulWidget, setState ve widget ağacı kavramlarını biliyorsanız Jaspr'ın öğrenme eğrisi çok sığdır çünkü component modeli neredeyse birebir aynıdır. Dart temellerini ve temel HTML/CSS bilgisini bilmeniz gerekir.
Hangi rendering modunu seçmeliyim?
İçerik odaklı siteler için (blog, dokümantasyon, tanıtım, portföy) static; girişli, kişiselleştirilmiş dinamik uygulamalar için server; SEO'nun gereksiz olduğu uygulama benzeri dashboard ve admin panelleri için client kullanın. Şablon seçimi sizi kilitlemez — modu sonradan pubspec.yaml'daki jaspr.mode ile değiştirebilirsiniz.
Jaspr'ın Material gibi hazır bir component kütüphanesi var mı?
Hayır. Material/Cupertino karşılığı stillendirilmiş bir component kütüphanesi yoktur. UI'ı CSS ile kendiniz kurarsınız; tip-güvenli Styles sınıfı, @css scoped stil veya Tailwind, Bulma, Sass gibi CSS framework'leri kullanabilirsiniz.
Jaspr'da neden Row ve Column widget'ları yok?
Tasarım tercihidir. Jaspr, web platformunun güçlü yanlarını gizlememek için Flutter'ın layout primitive'lerini bilerek kopyalamaz. Bunun yerine div gibi standart HTML elementlerini CSS flexbox ve grid ile kullanırsınız, bu da sizi web'e özgü teknolojilere yakın tutar.
Jaspr sunucu ve istemci arasında state'i nasıl senkronize eder?
@sync anotasyonu (bir State alanını pre-render sırasında sunucudan istemciye serialize eder, initState'te bir kez uygulanır) ve @client component constructor parametreleri (otomatik serialize edilir) aracılığıyla. Senkronizasyon tek yönlüdür (sunucu → istemci) ve yalnızca ilk render'da yapılır. İstemciden sunucuya veri göndermek için normal bir API kullanın.
Jaspr ile mevcut JavaScript kütüphanelerini kullanabilir miyim?
Evet. Jaspr JavaScript'e derlendiği için package:universal_web/js_interop.dart üzerinden @JS() anotasyonları ve extension type'larla JS interop yapabilirsiniz. Standart web API'leri için package:web/package:universal_web'i tercih edin; ham js_interop'u yalnızca harici JS kütüphaneleri ve ileri senaryolar için ayırın.
Bu rehber, resmi Jaspr dokümantasyonu (docs.jaspr.site, v0.23.x) temel alınarak hazırlanmıştır.