SvelteKit is fast, SEO-friendly, and ships no runtime overhead to the browser. For astrology app developers who need Google-indexable birth chart pages and horoscope content, SvelteKit's server-side rendering and built-in form actions make it the most productive framework available in 2026.
In this tutorial, we'll build a production-ready astrology app using SvelteKit and Vedika API — the only B2B astrology API with AI-powered conversational capabilities, 140+ endpoints, and Swiss Ephemeris astronomical precision. By the end, you'll have a fully server-rendered app with birth chart pages, reactive horoscope components, form actions, and an AI chatbot.
What You'll Build: A SvelteKit astrology app with server-side birth chart loading via +page.server.ts, reactive Svelte stores for birth data, form actions for user input, and an AI chat route powered by Vedika API. All API keys stay on the server — never exposed to the browser.
Why SvelteKit for Astrology Apps?
No Virtual DOM
Svelte compiles to vanilla JavaScript. No runtime framework overhead means faster Time to Interactive for your users.
Built-in SSR
+page.server.ts load functions run on the server. Birth chart pages are pre-rendered and Google-indexable by default.
Form Actions
Native progressive enhancement. Birth detail forms work without JavaScript, with full enhancement when JS loads.
Reactive Stores
$: reactive declarations and writable stores make birth data sync across components without prop drilling.
Prerequisites
- Node.js 20+ installed
- Basic knowledge of Svelte and TypeScript
- Vedika API key — get yours here
Step 1: Create the SvelteKit Project
Scaffold a new SvelteKit project with TypeScript:
npm create svelte@latest astrology-app
cd astrology-app
npm install
npm install -D @sveltejs/adapter-node
Configure Adapter and Environment
Update svelte.config.js to use the Node adapter for production deployment:
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter()
}
};
export default config;
Create a .env file at the project root — SvelteKit keeps these private automatically:
VEDIKA_API_KEY=vk_live_your_key_here
Step 2: Create the Vedika API Client (Server-Only)
Create a server-side utility that all load functions and API routes will share:
import { VEDIKA_API_KEY } from '$env/static/private';
const BASE = 'https://api.vedika.io';
export interface BirthDetails {
datetime: string;
latitude: number;
longitude: number;
timezone: string;
}
async function vedikaFetch(path: string, body: unknown) {
const res = await fetch(`${BASE}${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': VEDIKA_API_KEY
},
body: JSON.stringify(body)
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.message || `Vedika API error ${res.status}`);
}
return res.json();
}
export async function getBirthChart(birth: BirthDetails) {
return vedikaFetch('/v2/astrology/birth-chart', {
datetime: birth.datetime,
latitude: birth.latitude,
longitude: birth.longitude,
timezone: birth.timezone
});
}
export async function getDailyHoroscope(sign: string, lang = 'en') {
return vedikaFetch('/v2/astrology/horoscope/daily', { sign, language: lang });
}
export async function askVedika(question: string, birth: BirthDetails, language = 'en') {
return vedikaFetch('/api/v1/astrology/query', {
question,
language,
birthDetails: {
datetime: birth.datetime,
latitude: birth.latitude,
longitude: birth.longitude,
timezone: birth.timezone
}
});
}
Step 3: Birth Detail Form with SvelteKit Form Actions
Form actions let users submit birth details — and the form works even without JavaScript thanks to progressive enhancement:
import { fail } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types';
import { getBirthChart } from '$lib/server/vedika';
export const load: PageServerLoad = async ({ url }) => {
const dt = url.searchParams.get('dt');
const lat = url.searchParams.get('lat');
const lng = url.searchParams.get('lng');
const tz = url.searchParams.get('tz') || '+05:30';
if (dt && lat && lng) {
const chart = await getBirthChart({
datetime: dt,
latitude: parseFloat(lat),
longitude: parseFloat(lng),
timezone: tz
});
return { chart };
}
return { chart: null };
};
export const actions: Actions = {
default: async ({ request }) => {
const form = await request.formData();
const datetime = form.get('datetime') as string;
const latitude = parseFloat(form.get('latitude') as string);
const longitude = parseFloat(form.get('longitude') as string);
const timezone = form.get('timezone') as string || '+05:30';
if (!datetime || isNaN(latitude) || isNaN(longitude)) {
return fail(400, { error: 'Please fill all birth details' });
}
try {
const chart = await getBirthChart({ datetime, latitude, longitude, timezone });
return { success: true, chart };
} catch (e: any) {
return fail(500, { error: e.message });
}
}
};
Birth Chart Svelte Component
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageData, ActionData } from './$types';
export let data: PageData;
export let form: ActionData;
$: chart = form?.chart || data.chart;
$: planets = chart?.data?.planets || [];
$: ascendant = chart?.data?.ascendant;
</script>
<svelte:head>
<title>Birth Chart Calculator | Vedika Astrology</title>
<meta name="description" content="Generate your Vedic birth chart with Swiss Ephemeris precision." />
</svelte:head>
<main class="container">
<h1>Your Vedic Birth Chart</h1>
<form method="POST" use:enhance>
<label>
Date & Time of Birth
<input type="datetime-local" name="datetime" required />
</label>
<label>
Latitude
<input type="number" name="latitude" step="0.0001"
placeholder="28.6139" required />
</label>
<label>
Longitude
<input type="number" name="longitude" step="0.0001"
placeholder="77.2090" required />
</label>
<label>
Timezone (UTC offset)
<input type="text" name="timezone" value="+05:30" />
</label>
<button type="submit">Generate Chart</button>
</form>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
{#if chart}
<section class="chart-result">
<h2>Ascendant: {ascendant?.sign}</h2>
<table>
<thead>
<tr>
<th>Planet</th><th>Sign</th><th>House</th><th>Degree</th><th>Status</th>
</tr>
</thead>
<tbody>
{#each planets as p}
<tr>
<td>{p.name}</td>
<td>{p.sign}</td>
<td>{p.house}</td>
<td>{p.degree?.toFixed(2)}°</td>
<td>{p.isRetrograde ? 'Retrograde' : 'Direct'}</td>
</tr>
{/each}
</tbody>
</table>
</section>
{/if}
</main>
Step 4: Svelte Stores for Birth Data
A writable store lets any component across the app access and update the user's birth details reactively:
import { writable, derived } from 'svelte/store';
export interface BirthData {
datetime: string;
latitude: number;
longitude: number;
timezone: string;
locationName: string;
}
function createBirthStore() {
const { subscribe, set, update } = writable<BirthData | null>(null);
return {
subscribe,
setData(data: BirthData) {
set(data);
if (typeof window !== 'undefined') {
sessionStorage.setItem('vedika_birth', JSON.stringify(data));
}
},
clear() { set(null); }
};
}
export const birthData = createBirthStore();
export const birthReady = derived(
birthData,
($b) => !!$b?.datetime && !!$b?.latitude && !!$b?.longitude
);
Using the Store in Components
<script lang="ts">
import { birthData, birthReady } from '$lib/stores/birthData';
let prediction = '';
let loading = false;
$: if ($birthReady && $birthData) {
fetchPrediction($birthData);
}
async function fetchPrediction(birth: typeof $birthData) {
loading = true;
const res = await fetch('/api/horoscope', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ birth })
});
const data = await res.json();
prediction = data.prediction || data.horoscope || '';
loading = false;
}
</script>
{#if $birthReady}
{#if loading}
<p>Loading your prediction...</p>
{:else if prediction}
<div class="prediction">
<h3>Your Daily Prediction</h3>
<p>{prediction}</p>
</div>
{/if}
{:else}
<p>Enter your birth details to see your daily prediction.</p>
{/if}
Step 5: Server API Route for Horoscope
Create a SvelteKit server route that proxies requests to Vedika — keeping the API key server-side:
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { getDailyHoroscope } from '$lib/server/vedika';
export const POST: RequestHandler = async ({ request }) => {
const { sign, language } = await request.json();
if (!sign) {
return json({ error: 'sign is required' }, { status: 400 });
}
const data = await getDailyHoroscope(sign, language || 'en');
return json(data);
};
Step 6: AI Chat Route with Streaming
Create an API route that streams Vedika's AI responses for a real-time chat experience:
import type { RequestHandler } from './$types';
import { VEDIKA_API_KEY } from '$env/static/private';
export const POST: RequestHandler = async ({ request }) => {
const { question, birth, language } = await request.json();
const upstream = await fetch('https://api.vedika.io/api/v1/astrology/query/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': VEDIKA_API_KEY
},
body: JSON.stringify({
question,
language: language || 'en',
birthDetails: {
datetime: birth.datetime,
latitude: birth.latitude,
longitude: birth.longitude,
timezone: birth.timezone || '+05:30'
}
})
});
return new Response(upstream.body, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache'
}
});
};
Chat Component Consuming the SSE Stream
<script lang="ts">
import { birthData } from '$lib/stores/birthData';
let messages: { role: 'user' | 'ai'; text: string }[] = [];
let input = '';
let streaming = false;
async function send() {
if (!input.trim() || streaming || !$birthData) return;
const question = input;
input = '';
messages = [...messages, { role: 'user', text: question }];
messages = [...messages, { role: 'ai', text: '' }];
streaming = true;
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ question, birth: $birthData })
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(l => l.startsWith('data: '));
for (const line of lines) {
const text = line.slice(6);
if (text === '[DONE]') break;
try {
const { token } = JSON.parse(text);
if (token) {
messages[messages.length - 1].text += token;
messages = [...messages];
}
} catch {}
}
}
streaming = false;
}
</script>
<div class="chat">
<div class="messages">
{#each messages as m}
<div class="message {m.role}">{m.text}</div>
{/each}
{#if streaming}
<div class="typing-indicator">Vedika AI is thinking...</div>
{/if}
</div>
<form on:submit|preventDefault={send}>
<input bind:value={input} placeholder="Ask about your birth chart..." />
<button type="submit" disabled={streaming}>Send</button>
</form>
</div>
Ready to Build Your SvelteKit Astrology App?
Try the FREE Sandbox for development — 65 mock endpoints, no credit card required. Production plans from $12/month.
Get Your API Key
Step 7: Layout and SEO with <svelte:head>
SvelteKit makes per-page SEO trivial. Add dynamic meta tags based on server-loaded chart data:
<script lang="ts">
import type { LayoutData } from './$types';
export let data: LayoutData;
</script>
<svelte:head>
<title>{data.pageTitle || 'Vedic Birth Chart'} | Vedika Astrology</title>
<meta name="description" content={data.pageDescription ||
'Generate your Vedic birth chart with Swiss Ephemeris precision.'} />
<link rel="canonical" href="https://vedika.io/chart" />
</svelte:head>
<slot />
Step 8: Multi-Language Support
Vedika API responds in 30 languages. Detect the browser locale from the Accept-Language header in your server load function:
import type { PageServerLoad } from './$types';
import { getDailyHoroscope } from '$lib/server/vedika';
export const load: PageServerLoad = async ({ request, url }) => {
const sign = url.searchParams.get('sign') || 'aries';
const acceptLang = request.headers.get('accept-language') || 'en';
const lang = acceptLang.split(',')[0].split('-')[0];
const horoscope = await getDailyHoroscope(sign, lang);
return { horoscope, sign, lang };
};
Production Deployment
Deploy the SvelteKit app to any Node.js host — Fly.io, Railway, or Google Cloud Run:
npm run build
node build
fly launch
fly deploy
gcloud run deploy astrology-app \
--source . \
--set-env-vars VEDIKA_API_KEY=vk_live_xxx \
--region asia-south1 \
--allow-unauthenticated
Why Choose Vedika API?
AI-Powered
The only astrology API with a built-in AI engine. Ask questions in plain English — no manual calculation logic.
140+ Endpoints
Birth charts, planetary positions, yogas, doshas, dashas, transits, Ashtakavarga, numerology, and more.
30 Languages
Hindi, Tamil, Telugu, Bengali, Gujarati, Marathi, Kannada, Arabic, and 22 more. Auto-detected from headers.
Swiss Ephemeris
Astronomical-grade planetary position accuracy — the same library used by professional astrologers worldwide.
MCP Server
World's first astrology MCP server. AI agents can call Vedika natively for automated predictions.
From $12/month
Starter plan covers indie developers. Scale to Business at $120/month for production traffic.
Pricing
Vedika API offers transparent pricing starting at just $12/month:
- Starter Plan: $12/month — Perfect for indie developers and prototypes
- Professional Plan: $60/month — For growing SvelteKit apps
- Business Plan: $120/month — For production apps with traffic
- Enterprise Plan: $240/month — Unlimited usage with top-up credits
All plans include the full API with 140+ endpoints, AI chatbot, and 30-language support. View detailed pricing
Conclusion
SvelteKit and Vedika API are a natural pairing for production astrology apps. SvelteKit handles the hard parts of web development — SSR, SEO, form progresssive enhancement, streaming — while Vedika handles the even harder parts of astrology: Swiss Ephemeris precision, 140+ calculations, and AI-powered natural language predictions.
- Server-side API calls:
+page.server.ts keeps your Vedika key off the client
- Form actions: Birth detail forms work with zero JavaScript
- Reactive stores: Birth data syncs across all components automatically
- SSR for SEO: Birth chart and horoscope pages are fully Google-indexable
- Streaming chat: Forward Vedika's SSE stream directly to the browser
Next steps:
- Get your Vedika API key at vedika.io/signup
- Read the full API documentation
- Try the free sandbox at vedika.io/sandbox
- Add Kundali matching and transit reports to your app
About Vedika Intelligence: Vedika is the only B2B astrology API with AI-powered chatbot capabilities, serving production apps worldwide. Our AI engine enables natural language astrology queries with Swiss Ephemeris verified precision, supporting both Vedic and Western astrology systems across 30 languages.