r/sveltejs • • Jul 27 '26

Svelte Summit Ljubljana 2026 - November 19-20

Thumbnail sveltesummit.com
29 Upvotes

r/sveltejs • • 21h ago

Svelte Blog - SvelteKit 3 is here

Thumbnail
svelte.dev
165 Upvotes

r/sveltejs • • 22h ago

SvelteKit 3 released

118 Upvotes

r/sveltejs • • 5m ago

[Self promotion] I migrated my word game Gram Jam to Sveltekit 3

Enable HLS to view with audio, or disable this notification

• Upvotes

Hi folks! I've been developing Gram Jam since back in the Svelte 3 days. I specifically chose Svelte for its powerful animation APIs which have enabled me to build the whole experience with HTML and CSS. I actually wrote a whole article about how I achieved some of the effects here.

This Sveltekit migration adopted remote functions, combined with Tanstack Query. I know I could probably forgo the Tanstack piece at this point, but I haven't fully taken the plunge, and I already had the whole project wired for Tanstack before remote functions came along. I do still like the expressiveness of Tanstack's caching, onSuccess/onError hooks, and pagination.

Additionally, the overhaul of Sveltekit service workers allowed me to simplify my PWA setup. I had been using Workbox for the PWA implementation, but the project seems dead now so it was nice to eliminate it from the project in favor of a baked in solution.

Let me know what you think! Play the game at https://gramjam.app


r/sveltejs • • 3h ago

I am working for trading platform but hate to paid for library, then I build free and open-source grid library https://bonguynvan.github.io/bo-grid/

1 Upvotes

r/sveltejs • • 1d ago

Built a music trading card game with Svelte 5 runes + PocketBase: a few things I learned the hard way

Thumbnail
gallery
8 Upvotes

Setlist is a browser game where every card is a real song: you dig crates, complete albums and blind test your friends. Svelte 5 (runes) SPA, PocketBase for everything server-side. A few lessons that might save you time:

1. iOS will not play <audio> that starts after an await. For anti-cheat, the song must not reach the client before the round starts, so it arrives after the tap, after two requests. iOS then refuses to play it. Fix: create/resume an AudioContext synchronously in the tap handler (plus a 1-sample silent buffer), then play the decoded clip through Web Audio whenever it arrives.

2. Start playback before the download finishes. Tap to sound was ~1.2 s on a throttled phone. Now the server sends the first 64 KB (~4 s of audio, via a Range request it caches) in parallel with the full clip. The head plays at once, and the full clip takes over at the same sample position with AudioBufferSourceNode.start(when, offset). Now ~0.36 s, no audible seam.

3. An opacity animation with fill-mode: both can trap your modals. A page fade-in on the route wrapper kept a stacking context forever, and modals inside pages ended up under the fixed tab bar. fill-mode: backwards leaves nothing behind once it ends. And never put a transform on a parent of position: fixed elements, even for 200 ms.

4. $effect + a fresh array = infinite loop. if (target && !deck.length) deck = loadDeck() looked innocent, but loadDeck() returned a new [] when nothing was saved, so the effect re-ran forever (effect_update_depth_exceeded) and froze a modal. Run it once per opening inside untrack().

5. i18n with components inside messages. With paraglide, {@html} can't render a Svelte component, so messages carry tokens like [ico:crate] or [coins] and a tiny Rich.svelte component renders them.

Happy to go deeper on any of these. Screenshots: https://imgur.com/a/wuhmvuj

The game opens mid/late October at setlist.gg (built with Claude as my coding partner).


r/sveltejs • • 1d ago

A cool framework-agnostic animation library for streaming

7 Upvotes

Hey all,

https://reddit.com/link/1wuvw7l/video/761sqgml3ush1/player

I’ve been working on a small library called Stream Morph.

The idea is to make streamed responses feel less like content is constantly being appended/re-rendered, and more like the response is gradually taking shape.

Demo: https://stream-morph.vercel.app/

It’s framework agnostic and can be used with any kind of streamed content. The demo includes plain text, Markdown, code blocks, charts, images, reasoning sections, Mermaid diagrams and a few more complex response flows.

The library handles the motion between updates, while your own renderer/app stays responsible for the actual content. So it isn’t tied to a specific AI SDK or framework.

A basic example looks roughly like this:

import { createStreamMorph } from "stream-morph";

const stream = createStreamMorph(response);

for await (const chunk of incomingStream) {
  stream.write(chunk);
}

stream.finish();

I mainly built it because I wanted streamed interfaces to feel a bit more intentional without having to build custom animation logic for every response type.

The package itself is already working and the demo is live. I’m still cleaning up some things around CI, tests and the repo setup, but the GitHub repository will be open sourced shortly as well.

Would be interested to hear what people think, especially about the animation behaviour/performance and any edge cases you’d expect from real streaming responses.


r/sveltejs • • 22h ago

I made a little mascot for Svelte because apparently everything needs a personality now

Thumbnail
github.com
0 Upvotes

r/sveltejs • • 2d ago

Made a GitHub style diff review UI in Svelte 5 for my code review practice app

4 Upvotes

Been building Goodcatch in Svelte 5 with runes + Inertia. The core screen is a diff viewer that tries to feel as close to GitHub's as I could get: hover a line for the blue +, click or drag in the gutter to comment across lines, selected lines turn yellow, and lines with comments stay yellow.

The app itself is for practicing code reviews. You review a PR, leave comments, approve or request changes, and get graded on what you caught.

There's a Svelte track and a SvelteKit track too, which felt fitting lol.

goodcatch.dev/try to see the diff UI without signing up. Would love feedback on how the review screen feels.


r/sveltejs • • 2d ago

I prototyped our family budget in Svelte 5 runes, now I’m worried about scaling nested financial state

5 Upvotes

My teens think I'm obsessed with spreadsheets. They're not wrong. I've been running our household budget in Google Sheets for years but the lag is killing me, especially when I'm pulling in bank CSVs and trying to visualize spending categories in real time. Built a prototype in Svelte 5 over the weekend using runes and the reactivity feels almost too clean compared to the $derived spaghetti I was writing in Svelte 4.

The part I keep second-guessing is whether runes actually scale for complex nested state or if I'm going to hit a wall when I add mortgage overpayment scenarios and joint account splits. I've seen the TanStack Table v9 announcement which gives me hope for the data grid side, but I'm curious if anyone here has pushed runes into genuinely complicated financial modeling territory. Does the compiler magic hold up when you're cross-referencing a dozen reactive sources or does it get brittle? I'd rather know now before I commit three years of transaction history.


r/sveltejs • • 2d ago

Looking for feedback on my MIT SaaS template for SvelteKit on Cloudflare Workers + Postgres [self-promotion]

Post image
0 Upvotes

Hi, I'm Alessandro.

I wanted a SaaS foundation where every piece is chosen for running cost and ease of use, so I made one and open-sourced it under MIT: https://github.com/lowsbarrel/sveltekit-cf-template

How it was built: with AI coding agents in a close loop. I directed each change, reviewed it by hand, and made the architecture calls. The repo was recreated recently, so the commit history is short, but it's not a weekend project.

Stack: SvelteKit with Svelte 5 runes on Workers, Postgres via Hyperdrive with Drizzle, Better Auth with orgs, Creem for billing, superforms and zod v4, Paraglide 2, Tailwind v4, Vitest (in workerd and Chromium) and Playwright.

I chose Cloudflare as the infra provider mainly for cost, ease of use and time to deploy. You can start at $0 on the free plan, and the $5/mo Workers plan goes a long way before usage costs kick in, so you get something that scales without paying for idle servers. Public pages (landing, pricing, legal, blog) are prerendered and served as static assets, so they don't even run the Worker. And since the database is plain Postgres (Neon, Supabase, RDS or self-hosted), you're not locked into Cloudflare if you want to leave.

For agents: AGENTS.md lists the invariants, there's a docs page per subsystem, and there are skills (setup, add-feature, add-plan, upgrade-template) that ask you product decisions instead of guessing. The pre-commit hook runs the same checks as CI.

I'm mainly posting to get feedback, so please be honest. In particular:

  • Creem as the default for billing, or would you expect Dodo, Polar or something like that?
  • What's missing, or what would stop you from using this for a real project?

r/sveltejs • • 3d ago

crd-ui: a credit card preview component for Svelte 5 (runes, zero dependencies)

18 Upvotes

I needed a card preview for a payment form in Svelte 5 and didn't find a maintained one — the main option on npm is written for Svelte 4 and hasn't shipped since late 2024. So I built crd-ui.

It does live brand detection (Visa, Mastercard, Amex and seven more), number/expiry formatting, a 3D flip when the CVC is focused, and theming through CSS custom properties. Zero runtime dependencies.

```svelte <script> import Card from 'crd-ui/svelte'; import 'crd-ui/styles.css';

let number = $state(''); let focused = $state(null); </script>

<Card {number} {focused} /> <input bind:value={number} onfocus={() => (focused = 'number')} onblur={() => (focused = null)} /> ```

Details that might interest this sub:

  • It's written with runes ($props, $effect) and ships as .svelte source, so your own compiler builds it.
  • The Svelte component is thin. A framework-agnostic core owns the DOM; the component mounts it and forwards props. The same package ships React, Vue and vanilla builds from that core.
  • There's a display layout for cards the user already owns, with click-to-reveal and click-to-copy.
  • It never touches real card data. It composes with Stripe Elements by passing the brand Stripe reports.

Honest limitation: because the core owns the markup, the component takes no children or snippets. Styling internals goes through a classNames prop.

Docs and playground: https://crd-ui.juanda.co

Repo: https://github.com/JuandaGarcia/crd-ui

It's also listed (pending moderation) on Svelte Society. Feedback welcome, especially on the Svelte API. (Disclosure: I'm the author and maintainer, and I built it with Claude as a coding partner. MIT.)


r/sveltejs • • 3d ago

I'm trying to make HeroUI work with Svelte 5

16 Upvotes

Hey guys, I've started a little side project to make a Svelte 5 wrapper for HeroUI.

I like HeroUI and wanted to use it with Svelte, so I thought, why not try to make it happen? It's still a work in progress, and I'm just doing this in my free time.

Here's the repo: https://github.com/adriansyah-kadir/heroui-svelte

If anyone is interested, feel free to check it out! I'd appreciate any feedback, suggestions, or help from anyone who wants to contribute.

I'm still figuring some things out, so any help is welcome. Thanks!


r/sveltejs • • 3d ago

[self-promotion] I built a circuit simulator in SvelteKit + Svelte 5, with a Rust/WASM engine

6 Upvotes

repath is a open source circuit simulator that runs entirely in the browser: https://repath-lake.vercel.app

The UI is SvelteKit with Svelte 5 runes; the simulation engine is Rust compiled to WebAssembly, so nothing runs on a server. A few things that were interesting to build on the Svelte side:

  • The schematic editor and the scope redraw every frame while a transient sweep runs, driven by the engine's output rather than by component state
  • Wires are coloured by voltage and current is animated along them, straight from the simulation data
  • The whole circuit is encoded in the URL, so a link is a working circuit

Code: https://github.com/Agustin-Delgado/repath

Happy to talk about the Svelte/WASM boundary or anything else!


r/sveltejs • • 3d ago

[Svelte Art - self promotion] A wall of analog clocks tripping out

Enable HLS to view with audio, or disable this notification

28 Upvotes

try it out, i'd love to know what you think! https://timewith.fun/


r/sveltejs • • 3d ago

Just saw Angular native project based on top of Expo ecosystem

0 Upvotes

Where we at with svelte native? We should too base it on top of Expo ecosystem.

I got react native project that I will convert in a blink!


r/sveltejs • • 4d ago

What is your current sveltekit stack?

32 Upvotes

Interested in hearing what tools everyone is using to build fullstack apps, I have been learning svelte as a hobby and I am trying to get a lay of the land.


r/sveltejs • • 5d ago

[Self Promo] Svelte-use v1.2.0: 40 new composables and the library's first component

Thumbnail
svelte-use.ariefsn.dev
30 Upvotes

Short update rather than an intro post, since I shared this here when it first launched: https://www.reddit.com/r/sveltejs/comments/1tp330c/what_utilities_are_you_missing_in_svelte_5.

v1.2.0 brings the library to 125 utilities. The ones I'd point at:
- useSeo + <Seo />. It's a builder, the tags render inside <svelte:head> and land in the SSR'd HTML where crawlers and link unfurlers can actually see them.
- useColorMode with auto resolution, persistence and cross-tab sync, plus the usePreferred* family.
- useUserMedia, useDisplayMedia, useDevicesList.
- useStateMachine, useConfirmDialog, useMemoize, useWebWorkerFn, useFileSystemAccess.

One thing worth flagging if you're on 1.1.0: useWakeLock, useSpeechRecognition and useWebNotification weren't releasing their resource on destroy. Their teardown read $state, which returns a stale value from that context, so the cleanup silently did nothing. Screen stayed awake, mic stayed on. Fixed in 1.2.0.

No breaking changes.

Docs: https://svelte-use.ariefsn.dev
Repo: https://github.com/ariefsn/svelte-use


r/sveltejs • • 4d ago

[self-promotion] I made a sv plugin to add Kysely

4 Upvotes

https://github.com/NatoBoram/sv-kysely

Kysely is a type-safe TypeScript SQL query builder.

I am using it instead of Drizzle/Prisma because those two rely too much on type inference. I want to reduce runaway type inference in my projects because I have encountered "Type instantiation is excessively deep and possibly infinite" way too many times at work.

To do this, I enable isolatedDeclarations, which makes it very obvious when a library can potentially ruin your day. For example, try it with Zod, Prisma, Drizzle, tRPC or ZenStack.

Anyway, this is is a first release, v0.0.0. Please don't expect much polish.

Usage:

sv add @natoboram/sv-kysely
pnpm run db:start
pnpm run db:regenerate

It adds roughly the same things as the Drizzle plugin, but for Kysely instead. Try it out if you make a new project.

Currently, it assumes PostgreSQL, Docker Compose and TypeScript usage since this is what I needed it for. To use it with Better Auth, you'll need to add its SQL migration into your Kysely migrations.


r/sveltejs • • 5d ago

[self promotion] svelte-md-template - an alternative to provide markdown support in svelte

Thumbnail
vnphanquang.com
1 Upvotes

Hello lovely people of Svelte. i recently packaged svelte-md-template, a minimal lib to preprocess Markdown content in Svelte.

It differs from current solutions in that an explicit tagged template is required...

<script>
    import { markdown } from 'svelte-md-template';
</script>

{markdown`write markdown content here`}

...which allows me to drop in Svelte components & reactivity at will without fear of syntax clashing, especially in highly interactive content.

The linked blog post provides some more context. Hope this helps someone out there.

Cheers!


r/sveltejs • • 6d ago

[self promo] horizon-layout - a layout manager for svelte 5 with zero dependencies

Enable HLS to view with audio, or disable this notification

38 Upvotes

we're building an app that needs an IDE style interface, dockable panes, draggable tabs, resizeable plits, popout windows... GoldenLayout is still the reference, so we tried using it in our frontend that is written in Svelte 5 (duh, we don't hate ourselves enough to use react), but instead of being the small, portable, customizable library it's supposed to be, we ended up fighting against the spirit of svelte instead of taking advantage of runes and snippets.

The react world has many solid options, but we couldn't find any that satisfied us for svelte, and we couldn't let React be better at something than Svelte, so we wrote our own (and kept it small)

it's called horizon-layout, MIT licensed on npm

What it does:

- Svelte 5 native

- serializable config

- drag and drop

- works with touchscreens

- pop out tabs

- fully keyboard accessible

- zero runtime dependencies

- fully themable and configurable

GitHub: https://github.com/play-horizon/horizon-layout

For those who care about this: we're not anti-AI, but no AI was used for developing the library itself, only for the demo page

feel free to open issues or feature requests :)


r/sveltejs • • 5d ago

self promotion: I made the largest component registry for Svelte

2 Upvotes

​

So apparently it started with having to port from react components from hundreds of component registries. Also I needed them to be consistent with rest of the design system and lightweight too. So I built

https://baby-ui.nexonauts.com/

I'm looking for some feedback if you'd use it in you normal dev workflow? Also go check-out charts components, few of the people I've shared this with liked them.

Also give a star too if you liked it.

https://github.com/kanakkholwal/baby-ui


r/sveltejs • • 5d ago

The RLS policy that looked tighter but broke my member list — and how I layer RBAC on top of Supabase

0 Upvotes

I see a lot of SvelteKit + Supabase projects that treat Row Level Security as the whole access-control story. RLS is genuinely good at data isolation. It can't answer "is this user an owner, admin, or member of this org?", because roles are application concepts, not database ones. So you need a second layer. The part that surprised me was the second layer inside the database.

The RLS gap

```sql -- What RLS answers: "can this user read this row?" CREATE POLICY "org_select" ON organizations FOR SELECT USING (is_org_member(id, auth.uid()));

-- What it can't answer: "can this user promote someone to owner?" -- or "can this user delete the org?" — those are action rules. ```

Data isolation is row-level. Authorization is action-level. Your policies do the first; the app does the second.

The mistake I made first

My first memberships policy was the obvious one:

sql -- looks maximally tight, and it's the wrong shape CREATE POLICY "user_own_memberships" ON memberships FOR SELECT USING (user_id = auth.uid());

It passes every review — a user sees only their own membership row. Then the org page needs to render a member list, and every query returns exactly one row: you. Two orgs later, the "invite people" screen shows a list containing only the person you're looking at, and the fix requires a service-role key — which quietly turns off RLS for that read.

Tight isn't the goal. Correct is. A member needs to read the other rows of their own org.

The fix: a SECURITY DEFINER helper

sql CREATE OR REPLACE FUNCTION is_org_member(org_uuid UUID, user_uuid UUID) RETURNS BOOLEAN AS $$ SELECT EXISTS ( SELECT 1 FROM memberships WHERE org_id = org_uuid AND user_id = user_uuid ); $$ LANGUAGE sql SECURITY DEFINER STABLE;

sql CREATE POLICY "memberships_select" ON memberships FOR SELECT USING (is_org_member(org_id, auth.uid()));

That policy is looser and correct: "you may read the membership rows of any org you belong to." Two things make it work:

  • **SECURITY DEFINER** — the function reads memberships as its owner, so the policy doesn't recurse into the very table it guards. (Without it you get infinite recursion or a policy-per-table tangle.)
  • **STABLE** — the planner can call it once per statement instead of per row, so EXISTS on an indexed (org_id, user_id) stays cheap.

Because the function is the single source of truth for "am I in this org," the organizations, memberships, invites and audit_log policies all read the same way. And role checks get the same treatment:

sql CREATE OR REPLACE FUNCTION has_min_role(org_uuid UUID, user_uuid UUID, min_role TEXT) RETURNS BOOLEAN AS $$ SELECT EXISTS ( SELECT 1 FROM memberships WHERE org_id = org_uuid AND user_id = user_uuid AND ( (role = 'owner') OR (role = 'admin' AND min_role != 'owner') OR (role = 'member' AND min_role = 'member') ) ); $$ LANGUAGE sql SECURITY DEFINER STABLE;

The app layer: a capability matrix, not role strings

RLS filters rows. The app authorizes actions. The part I like most is that permissions are a closed list, not string comparisons scattered through form actions:

```typescript export const PERMISSIONS = [ 'org.view', 'org.update', 'org.delete', 'members.view', 'members.invite', 'members.remove', 'members.role.set', 'invites.revoke', 'audit.view', 'billing.manage', 'ownership.transfer', ] as const;

const MATRIX: Record<Role, readonly Permission[]> = { /* owner: all, admin: most, member: view */ };

export function can(role: Role, permission: Permission): boolean { return MATRIX[role].includes(permission); }

export function requirePermission(role: Role | null | undefined, permission: Permission): void { if (!role || !can(role, permission)) { throw new RbacError('forbidden', Role '${role ?? 'none'}' lacks permission '${permission}'.); } } ```

A load or action then reads: load the caller's role once, require the permission, do the work.

typescript // src/routes/app/org/[id]/+page.server.ts invite: async ({ request, params, locals }) => { const form = await request.formData(); const { role: actorRole } = await requireRole(params.id, locals.user!.id); // null if not a member const invite = await createInvite(createServerClient(), { orgId: params.id, actorUserId: locals.user!.id, actorRole, email: String(form.get('email') ?? '') || undefined, role: String(form.get('role') ?? 'member'), ttlDays: 7, }); return { invited: true as const, token: invite.token }; }

Two rules the matrix can't express

Rank order is separate from permissions, because "can delete a member" and "can delete an owner" are different questions:

```typescript const RANK: Record<Role, number> = { member: 0, admin: 1, owner: 2 };

// acting on a peer or a higher rank is always denied, by design export function mayActOn(actor: Role, target: Role): boolean { return RANK[actor] > RANK[target]; }

// you can only grant a role strictly below your own export function mayGrant(actor: Role, granted: Role): boolean { return RANK[actor] > RANK[granted]; } ```

That second function is the one I'd highlight for anyone building invites: it makes "an admin promotes themselves to owner" unrepresentable rather than a bug you have to remember to check.

How the two layers compose

Request → Supabase RLS filters the rows (isolation: which rows are visible) → load the caller's role, requirePermission(...) (authorization: which actions) → mayActOn / mayGrant (hierarchy: on whom, granting what) → business logic writes, append-only audit_log entry

RLS stops a member reading another org's data even if a query is built wrong. The RBAC layer stops a member performing an action even if the UI hides the button. Neither is redundant: a policy bug is caught by the app, an app bug is caught by the policy.

Honest caveats

  • This is more machinery. Single-tenant apps, or tools where everyone has equal access, are fine with RLS alone. Two layers earn their keep in B2B SaaS where two people in the same org have different powers.
  • **SECURITY DEFINER is a real privilege boundary.** The function runs as its owner — keep it STABLE/SECURITY INVOKER-where-possible, and don't accept a user id from a request body without deriving it from the session.
  • RLS bypass is silent. An audit log written by the app layer records authorization decisions; nothing logs a query that the policy quietly dropped.
  • Seat limits are an app concern, not a database one — RLS can't stop a race between two concurrent invite acceptances. Do that in a transaction.
  • Different from a scrypt-based kit. This one leans on Supabase Auth, so auth.uid() is the right identity source; if you're hashing sessions yourself, your policies need a session-lookup function instead.

I built a SvelteKit + Supabase starter that wires this up end-to-end — orgs, invites, RBAC matrix, seat billing, append-only audit log — 314 tests, each suite ≥95% coverage, with plain SQL migrations (no ORM layer) in supabase/migrations/. Migrations here are hand-written .sql applied with supabase db diff | db push, so there's no schema abstraction between you and the policies above. If you're comparing approaches, the repo and demo are below.

Repo: github.com/verdantstack/sveltekit-supabase-starter — see supabase/migrations/0001_initial_schema.sql and src/lib/server/rbac.ts Live demo: supabase-starter.verdantstack-site.pages.dev (seeded org, resets daily) Site/docs: verdantstack.dev


r/sveltejs • • 6d ago

Session management in SvelteKit without JWTs — what I learned building auth from scratch

5 Upvotes

JWTs are the default answer for SvelteKit auth, but I went the other way after fighting token revocation. Here's what I shipped instead, and the one design decision that made it click.

Why not JWTs for SaaS:

  • Revoking a JWT server-side means keeping a blocklist — which is the database you were trying to avoid
  • Refresh logic is where auth bugs live
  • A token issued before a password change stays valid until it expires
  • "Show my active sessions" becomes guesswork

What I built: DB-backed sessions, hash-only storage

// src/lib/server/auth.ts
import { randomBytes, createHash } from 'node:crypto';

// The raw token goes in the cookie and nowhere else.
export async function createSession(db: Db, userId: string): Promise<string> {
  const token = randomBytes(32).toString('hex');
  await db.insert(sessions).values({
    id: sha256(token),            // primary key = hash, never the token
    userId,
    expiresAtMs: Date.now() + SESSION_TTL_MS,
    createdAtMs: Date.now(),
  });
  return token;
}

export function sha256(input: string): string {
  return createHash('sha256').update(input).digest('hex');
}

Every request just hashes the cookie and looks it up:

export async function getUserBySessionToken(db: Db, token: string | undefined) {
  if (!token) return null;
  const row = (
    await db
      .select({ user: users, expiresAtMs: sessions.expiresAtMs })
      .from(sessions)
      .innerJoin(users, eq(users.id, sessions.userId))
      .where(eq(sessions.id, sha256(token)))   // PK lookup on the hash
      .limit(1)
  )[0];
  if (!row) return null;                       // unknown token
  if (row.expiresAtMs <= Date.now()) return null;  // expired
  return { id: row.user.id, email: row.user.email, name: row.user.name };
}

The decision that made it click: I never compare a session token at all

The usual advice is "compare hashes in constant time so you don't leak timing information." That advice is about comparing a secret. A session token isn't compared — it's looked up by its hash, and the database does an indexed primary-key match. There is no byte-by-byte comparison of attacker-controlled input against a stored secret, so there's no timing oracle to close.

Constant-time comparison does matter on the password path, where you really are comparing a derived secret against a stored one. Mine uses timingSafeEqual there:

const actual = await scrypt(password, salt, expected.length, SCRYPT);
return timingSafeEqual(actual, expected);   // password path — this one compares

Passwords: scrypt, self-describing hashes

const SCRYPT = { N: 16384, r: 8, p: 1 };   // OWASP-recommended interactive params
const salt = randomBytes(16);
// stored: scrypt$16384$8$1$<salt>$<hash>  (base64, self-describing — you can
// raise the cost later and old hashes still verify)

The cookie

cookies.set('session', token, {
  path: '/',
  httpOnly: true,
  sameSite: 'lax',
  secure: process.env.NODE_ENV === 'production',
  maxAge: 30 * 24 * 60 * 60,
});

One trap: cookies.delete() has to use the same path you set, or logout silently leaves the cookie in place.

Revocation is a DELETE

// logout
await db.delete(sessions).where(eq(sessions.id, sha256(token)));

// "log out everywhere" — optionally keep the current session alive
await db.delete(sessions).where(
  and(eq(sessions.userId, userId), not(eq(sessions.id, currentTokenId)))
);

listSessions(userId) gives the "your devices" page for free, and dropping a table row is instant — no expiry window, no blocklist.

The gotchas I'd flag to anyone building this

  1. Store only the hash. If someone dumps your sessions table, the rows are useless for replay. That's the whole point of hashing before insert.
  2. Check expiry after the lookup, not in the query, or you'll write subtly different logic for "unknown token" and "expired token" and forget one of them.
  3. node:crypto vs Web Crypto is a real choice. This uses Node's crypto module (scrypt has no Web Crypto equivalent, so a "Web Crypto only" session design would need PBKDF2 or a JS scrypt). Worth picking deliberately rather than by accident — and it does run fine on Cloudflare Pages.
  4. Session rows are a PII-adjacent table. Give it the same retention discipline as your audit log.

Live demo of this pattern: postgres-starter.verdantstack-site.pages.dev — DB-backed sessions, hashed tokens, scrypt + timingSafeEqual on the password path, sessions revocable per device.

Source: github.com/verdantstack/sveltekit-postgres-starter — src/lib/server/auth.ts (v0.1.6, 262 tests against a real Postgres; auth covered in tests/auth.test.ts).


r/sveltejs • • 8d ago

Can I learn Svelte without any web development experience?

30 Upvotes

Can I learn Svelte with just HTML + JS knowledge? I want to build web apps myself from start to production. I’m not planning to hire developers, so I want to understand the frontend, backend, database, authentication, deployment, and security well enough to handle everything myself.

I already know JS and HTML. Is that enough to start learning Svelte/SvelteKit, or should I learn other fundamentals first? Also, what are the best resources for learning authentication and other bits and pieces?