DesQTA is built on SvelteKit 5 , the latest version of the SvelteKit framework. SvelteKit provides file-based routing, server-side rendering capabilities, and a powerful development experience.
DesQTA follows SvelteKit's standard directory structure:
src/
├── routes/ # File-based routing
│ ├── +layout.svelte # Root layout
│ ├── +page.svelte # Homepage
│ ├── +error.svelte # Error page
│ ├── assessments/ # Assessments route
│ │ └── +page.svelte
│ └── [dynamic]/ # Dynamic routes
├── lib/ # Shared code
│ ├── components/ # Reusable components
│ ├── services/ # Business logic
│ ├── stores/ # State management
│ └── utils/ # Utilities
└── app.css # Global styles
SvelteKit uses the file system to define routes:
+page.svelte : Page component+layout.svelte : Layout wrapper (applies to child routes)+error.svelte : Error boundary+loading.svelte : Loading state
// src/routes/+page.svelte
// Renders at: /
// src/routes/assessments/+page.svelte
// Renders at: /assessments
// src/routes/assessments/[id]/+page.svelte
// Renders at: /assessments/:id
<!-- src/routes/+layout.svelte -->
< div class = "app-layout" >
< AppHeader />
< AppSidebar />
< slot /> <!-- Child routes render here -->
</ div >
<!-- src/routes/assessments/+page.svelte -->
<!-- Automatically wrapped by +layout.svelte -->
< div >Assessments content</ div >
DesQTA uses Svelte 5 runes for reactive state management. Runes are compile-time primitives that provide better performance and type safety.
// Component state
let count = $state ( 0 );
let user = $state < User | null >( null );
let items = $state < string []>([]);
// Update state
count = 10 ;
user = { id: 1 , name: 'John' };
items = [ ... items, 'new item' ];
Key Points:
Automatically reactive Type-safe No need for $: reactive statements Works with any data type
// Computed from state
let doubled = $derived (count * 2 );
let filtered = $derived (items. filter ( i => i. startsWith ( 'a' )));
let fullName = $derived (user ? `${ user . firstName } ${ user . lastName }` : '' );
// Complex derived state
let sortedAssessments = $derived (
assessments
. filter ( a => a.dueDate >= today)
. sort (( a , b ) => a.dueDate - b.dueDate)
);
Key Points:
Automatically updates when dependencies change Memoized (only recomputes when needed) Can reference other $derived values Type-safe
// Run side effects when state changes
$effect (() => {
console. log ( 'Count changed:' , count);
// Update DOM, call API, etc.
});
// Cleanup function
$effect (() => {
const interval = setInterval (() => {
count ++ ;
}, 1000 );
return () => {
clearInterval (interval);
};
});
// Conditional effects
$effect (() => {
if (user) {
loadUserData (user.id);
}
});
Key Points:
Runs after DOM updates Can return cleanup function Automatically tracks dependencies Runs synchronously by default
// Define props interface
interface Props {
title : string ;
count ?: number ;
items : string [];
}
// Declare props
let { title, count = 0 , items } : Props = $props ();
// Use in template
< h1 >{title} </ h1 >
< p > Count : {count} </ p >
Key Points:
Type-safe props Optional props with defaults Destructured assignment No export let needed
< script lang = "ts" >
interface Props {
title : string ;
count : number ;
}
let { title, count } : Props = $ props ();
let localState = $ state ( 0 );
let doubled = $ derived (count * 2 );
$ effect (() => {
console. log ( 'Count changed:' , count);
});
</ script >
< div >
< h1 >{title}</ h1 >
< p >Count: {count}, Doubled: {doubled}</ p >
< button onclick ={() => localState ++ }>
Local: {localState}
</ button >
</ div >
<!-- Card.svelte -->
< div class = "card" >
< header >
< slot name = "header" />
</ header >
< main >
< slot /> <!-- Default slot -->
</ main >
< footer >
< slot name = "footer" />
</ footer >
</ div >
<!-- Usage -->
< Card >
< svelte : fragment slot = "header" >
< h2 >Title</ h2 >
</ svelte : fragment >
< p >Content</ p >
< svelte : fragment slot = "footer" >
< button >Action</ button >
</ svelte : fragment >
</ Card >
< script lang = "ts" >
function clickOutside ( node : HTMLElement ) {
function handleClick ( event : MouseEvent ) {
if ( ! node. contains (event.target as Node )) {
node. dispatchEvent ( new CustomEvent ( 'clickoutside' ));
}
}
document. addEventListener ( 'click' , handleClick);
return {
destroy () {
document. removeEventListener ( 'click' , handleClick);
}
};
}
</ script >
< div use : clickOutside on : clickoutside ={() => console. log ( 'Clicked outside' )}>
Content
</ div >
import { onMount } from 'svelte' ;
onMount ( async () => {
// Runs once when component mounts
const data = await fetchData ();
items = data;
// Return cleanup function
return () => {
// Cleanup on unmount
cleanup ();
};
});
import { onDestroy } from 'svelte' ;
let interval : ReturnType < typeof setInterval>;
onMount (() => {
interval = setInterval (() => {
count ++ ;
}, 1000 );
});
onDestroy (() => {
clearInterval (interval);
});
import { beforeUpdate, afterUpdate } from 'svelte' ;
beforeUpdate (() => {
// Runs before DOM updates
console. log ( 'Before update' );
});
afterUpdate (() => {
// Runs after DOM updates
console. log ( 'After update' );
});
While runes handle component state, DesQTA uses Svelte stores for global state:
// src/lib/stores/theme.ts
import { writable } from 'svelte/store' ;
export const theme = writable < 'light' | 'dark' | 'system' >( 'system' );
export const accentColor = writable ( '#3b82f6' );
// Usage in components
import { theme, accentColor } from '$lib/stores/theme' ;
// Subscribe
$ : currentTheme = $theme;
$ : currentAccent = $accentColor;
// Update
theme. set ( 'dark' );
accentColor. set ( '#ff0000' );
import { derived } from 'svelte/store' ;
export const themeProperties = derived (
[currentTheme, themeManifest],
([ $currentTheme , $manifest ]) => {
if ( ! $manifest) return {};
return $manifest.customProperties;
}
);
// src/routes/assessments/+page.ts
import type { PageLoad } from './$types' ;
export const load : PageLoad = async ({ fetch , depends }) => {
depends ( 'app:assessments' );
const response = await fetch ( '/api/assessments' );
const assessments = await response. json ();
return {
assessments
};
};
// Use in +page.svelte
< script lang = "ts" >
import type { PageData } from './$types' ;
let { data } : { data : PageData } = $props ();
// data.assessments is available
</ script >
// src/routes/+layout.ts
export const load = async () => {
const user = await getUser ();
return { user };
};
// Available in all child routes
<!-- src/routes/+error.svelte -->
< script lang = "ts" >
import { page } from '$app/stores' ;
import { onMount } from 'svelte' ;
let { error, status } : { error : Error ; status : number } = $ props ();
</ script >
< div class = "error-page" >
< h1 >{status}</ h1 >
< p >{error.message}</ p >
< a href = "/" >Go Home</ a >
</ div >
let error = $state < string | null >( null );
try {
const data = await fetchData ();
items = data;
} catch (e) {
error = e instanceof Error ? e.message : 'Unknown error' ;
}
{# if error}
< ErrorMessage {error} />
{ /if }
// src/routes/login/+page.server.ts
import { fail } from '@sveltejs/kit' ;
import type { Actions } from './$types' ;
export const actions : Actions = {
default : async ({ request }) => {
const data = await request. formData ();
const email = data. get ( 'email' );
const password = data. get ( 'password' );
if ( ! email || ! password) {
return fail ( 400 , { error: 'Missing fields' });
}
// Authenticate
const user = await authenticate (email, password);
return { success: true , user };
}
};
// In +page.svelte
< form method = "POST" >
< input name = "email" type = "email" />
< input name = "password" type = "password" />
< button type = "submit" > Login </ button >
</ form >
< script lang = "ts" >
let promise = $ state ( fetchData ());
async function refetch () {
promise = fetchData ();
}
</ script >
{# await promise}
< LoadingSpinner />
{: then data}
< DataDisplay { data } />
{: catch error}
< ErrorMessage { error } />
{/ await }
{# key selectedId}
< DetailView id ={selectedId} />
{/ key }
<!-- Component.svelte -->
< script lang = "ts" >
let { children, header } : {
children : import ( 'svelte' ). Snippet ;
header ?: import ( 'svelte' ). Snippet ;
} = $ props ();
</ script >
< div >
{# if header}
{@ render header ()}
{/ if }
{@ render children ()}
</ div >
// ✅ Good - Use $state
let count = $state ( 0 );
// ❌ Avoid - Don't use reactive statements
let count = 0 ;
$ : doubled = count * 2 ;
// ✅ Good - Single responsibility
< AssessmentCard {assessment} />
// ❌ Avoid - Too many responsibilities
< AssessmentCardWithFiltersAndSorting />
// ✅ Good - Business logic in service
import { assessmentService } from '$lib/services/assessmentService' ;
const data = await assessmentService. loadAssessments ();
// ❌ Avoid - Logic in component
const data = await fetch ( '/api/assessments' ). then ( r => r. json ());
// ✅ Good - Type everything
interface Assessment {
id : number ;
title : string ;
dueDate : Date ;
}
// ❌ Avoid - Any types
let assessment : any ;