# Kom igång med Svelte

## Förutsättningar

* [@soleil-se/build-app](/build/app)
* [@soleil-se/app-util](/packages/app-util).
* [@soleil-se/eslint-config](/packages/eslint-config).

Lägg gärna några timmar på [tutorialen](https://svelte.dev/tutorial/svelte/welcome-to-svelte) för att komma igång!

## Installation

### App

Boilerplate för en ny Svelte App kan skapas med [@soleil-se/create-app](/build/create-app/).

```sh
npm init @soleil-se/app@latest
```

### Projekt

Enklast för att komma igång är att titta på [KEX 2](https://github.com/soleilit/kex-2) att se struktur och inställningar.

Nedan finns en steg för steg guide och förklaring till de filer som behövs att installera i ett projekt.

Installera i projekt**

**1. Installera dependencies**\
För att kunna kompilera Svelte installera `svelte` och `svelte-preprocess`.

```sh
npm i svelte svelte-preprocess --dev
```

Förutsätter att [@soleil-se/eslint-config](/packages/eslint-config) finns installerat och uppdaterat.

**2. Filer i rotkatalogen**\
Skapa upp eller uppdatera innehållet i följande filer i rotkatalogen.

* client\_src/

  * …

* server\_src/

  * …

* eslint.config.js

* jsconfig.json

* package.json

* prettier.config.js

* stylelint.config.js

* svelte.config.js

`eslint.config.js`\
Konfiguration för ESLint, här använder vi [@soleil-se/eslint-config](/packages/eslint-config).

**.eslint.config.js**

```js
import config from '@soleil-se/eslint-config';


export default config;
```

`prettier.config.js`\
Inställningar för Prettier som används som för att formatera kod i Svelte-filer.\
Läs mer under [Kodformatering](#kodformatering)

**prettier.config.js**

```js
import config from '@soleil-se/eslint-config/prettier';


export default config;
```

`stylelint.config.js`\
Konfiguration för Stylelint, här använder vi [@soleil-se/stylelint-config](/packages/stylelint-config/).

**stylelint.config.js**

```js
export default {
  extends: '@soleil-se/stylelint-config',
};
```

`jsconfig.json`\
Används för att sätta var roten av projektet eller appen finns, detta för att språkservicen ska veta var den ska leta efter beroenden.\
Läs mer om [jsconfig.json](https://code.visualstudio.com/docs/languages/jsconfig).

**jsconfig.json**

```json
{
  "exclude": ["node_modules", "dist"]
}
```

`svelte.config.js`\
Används bland annat för att ställa in preprocessing, exempelvis Sass eller Less, samt kompilatorinställningar.\
Läs mer under [Preprocessing](#preprocessing).

**svelte.config.js**

```js
import { sveltePreprocess } from 'svelte-preprocess';


const ignoredWarnings = ['state_referenced_locally'];


export default {
  preprocess: sveltePreprocess(),
  compilerOptions: {
    warningFilter(warning) {
      return !ignoredWarnings.includes(warning.code);
    },
  },
};
```

## Visual Studio Code

Installera [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode) tillägget till Visual Studio Code för att få korrekt highlighting, formatering osv.

## ESLint

Svelte filer lintas automatiskt om man har `eslint` och [@soleil-se/eslint-config](/packages/eslint-config/) installerat och läser in denna konfiguration.

## Kodformatering

Formatering av kod i Svelte använder Prettier så man behöver lägga till inställningar för detta så HTML kan formateras, dessvärre påverkar detta även JavaScript så några regler som man är van vid har fått stängas av så det inte krockar med Prettier.

### Prettier

Skapa en fil som heter `prettier.config.js` i roten av projektet eller appen.

**prettier.config.js**

```js
import config from '@soleil-se/eslint-config/prettier';


export default config;
```

### Automatisk formatering

För att formatera automatiskt behöver man lägga till följande i `settings.json` i VS Code.

**settings.json**

```json
{
  "[svelte]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "svelte.svelte-vscode"
  }
}
```

Man kan även stänga av formateringen av skript genom att lägga till kommentaren\
`<!-- prettier-ignore -->` före skripttaggen om det blir krockar och problem.

```svelte
<!-- prettier-ignore -->
<script>
  const foo = 'bar';
</script>


<p>{foo}</p>
```

## Preprocessing

Vi använder ofta preprocessing av styling i våra appar med antingen Sass eller Less. För att få korrekta felmeddelanden behöver man berätta för Svelte att man använder en preprocessor.\
Skapa en fil som heter `svelte.config.js` i roten av appen.

Om du använder Sass i projektet**

Använder man appen i ett projekt med standardstrukturen är det vanlig att man enkelt vill kunna importera variabler och mixins.\
Följande konfiguration säger att det finns en till path där Sass ska leta efter filer.

**svelte.config.js**

```js
import { sveltePreprocess } from 'svelte-preprocess';


export default {
  preprocess: sveltePreprocess({
    scss: { includePaths: [`${import.meta.dirname}/../../../../client_src/sass`] },
  }),
};
```

Om du använder Less i projektet**

Använder man appen i ett projekt med standardstrukturen är det vanlig att man enkelt vill kunna importera variabler och mixins.\
Följande konfiguration säger att det finns en till path där Less ska leta efter filer.

**svelte.config.js**

```js
import { sveltePreprocess } from 'svelte-preprocess';


export default {
  preprocess: sveltePreprocess({
    less: { paths: ['../../../../client_src/less'] },
  }),
};
```

## Struktur

Struktur för en WebApp som använder Svelte.

> **VIKTIGT!**
>
> Filerna `jsconfig.json` och `svelte.config.js` måste finnas i varje app då dessa bland annat används för att sätta var roten av appen finns, detta för att språkservicen ska veta var den ska leta efter beroenden.
>
> Finns inte dessa filer kommer hela projektet letas igenom efter beroenden och beroende på projektets storlek kan detta ta väldigt lång tid.
>
> Har man med en `jsconfig.json` i projektet utan att man exkluderar `node_modules` katalogen kommer det även letas i dessa kataloger, vilket inte heller är optimalt.
>
> [Läs mer om jsconfig.json](https://code.visualstudio.com/docs/languages/jsconfig).

* config Konfiguration för appen.

  * App.svelte Huvudkomponent för konfigurationen
  * config.js Klientkod för konfigurationen
  * index.js Serverkod för konfigurationen

* config\_global Global konfiguration för appen

  * App.svelte Huvudkomponent för globala konfigurationen
  * config.js Klientkod för globala konfigurationen
  * index.js Serverkod för globala konfigurationen

* src

  * client/ Klientspecifik kod

    * …

  * common/ Universell kod

    * …

  * components/ Komponenter

    * …

  * server/ Serverspecifik kod

    * …

  * resource/ Mapp för appens resurser

    * …

  * App.svelte Huvudkomponent för appen

  * index.js Serverkod för appen

  * headless.js Headless kod för appen

  * hooks.js Pre render hooks

  * main.js Klientkod för appen

  * appDataDefaults.json Defaultvärden för konfiguration

* jsconfig.json

* manifest.json

* package.json

* svelte.config.js

**Filer**

jsconfig.json**

```json
{
  "exclude": ["node_modules", "dist"]
}
```

[Läs mer om jsconfig.json](https://code.visualstudio.com/docs/languages/jsconfig)

manifest.json**

Standard [manifest.json](https://developer.sitevision.se/docs/webapps/manifest) för WebApps:

```json
{
  "id": "se.soleil.mySvelteApp",
  "version": "1.0.0",
  "name": "Min Svelte App",
  "author": "Soleil AB",
  "description": "Min Svelte app",
  "helpUrl": "https://soleil.se/support",
  "type": "WebApp",
  "bundled": true
}
```

package.json**

```json
{
  "name": "my-app",
  "author": "Soleil AB",
  "license": "UNLICENSED",
  "private": true,
  "dependencies": {
    "@sitevision/api": "^2025.1.1",
    "@soleil-se/app-util": "^5.0.0",
    "svelte": "^5.0.0"
  },
  "devDependencies": {
    "@soleil-se/build-app": "^2.0.0"
  },
  "scripts": {
    "build": "build-app build",
    "watch": "build-app watch",
    "start": "build-app start",
    "deploy": "build-app deploy"
  }
}
```

## Rendering

Det finns tre sätt man kan rendera en Svelte app på:

1. Universellt, appen kommer renderas både på servern och klienten.
2. Serverside renderad (SSR), appen kommer bara renderas på servern.
3. Clientside renderat (CSR), appen kommer bara renderas på klienten.

När ska man använda vad?\
Blir alltid en avvägning efter vilka krav kunden har.

* Måste appen fungera utan JavaScript igång?\
  Använd **Universellt eller SSR**.
* Är appen statisk utan interaktivitet?\
  Använd **SSR**.
* Har appen mycket interaktivitet?\
  Använd **Universellt eller CSR**.
* Är det en kritisk app för att kunna besöka och använda webbplatsen?\
  Använd **Universellt eller SSR**.
* Finns det mycket dynamiskt innehåll eller interaktivitet som inte behöver indexeras eller som inte är kritisk för webbplatsen?\
  Använd **CSR**.

### Universell

En universell app kommer renderas både på servern och klienten.\
Först kommer statisk HTML skrivas ut på serversidan i Sitevision sedan kommer koden på klientsidan ta över den HTML som renderats på servern och lägga på interaktivitet.

index.js**

[Läs mer om render i index.js](/packages/app-util/svelte/#indexjs).

```js
import router from '@sitevision/api/common/router';
import { render } from '@soleil-se/app-util/server/svelte/5';


import App from './App.svelte';


router.get('/', (req, res) => {
  const props = { foo: 'bar' };
  const html = render(App, props);
  res.agnosticRender(html, props);
});
```

main.js**

[Läs mer om render i main.js](/packages/app-util/svelte/#mainjs).

```js
import { render } from '@soleil-se/app-util/client/svelte/5';
import App from './App.svelte';


export default (props, target) => {
  render(App, { props, target });
};
```

Komponenterna behöver därför fungera både på servern och klienten. Men det kan hända att man har kod som behöver kunna exekveras på klienten men inte servern, vilket är ganska vanligt då man oftast har interaktivitet i sin app om den behöver renderas på klienten.

Man kan då använda sig av [isBrowser](/packages/app-util/common/#isbrowser--boolean) från `@soleil-se/app-util`, vilken enbart kommer inkludera koden om man är på klientsidan. Motsvarande för servern finns också, se [isServer](/packages/app-util/common/#isserver--boolean).\
Man kan även tänka på att alla events och `onMount` enbart körs på klientsidan.

Exempel: Lägg på en klass på html-elementet**

`document` finns enbart tillgänglig på klientsidan.

```js
import { isBrowser } from '@soleil-se/app-util';


if (isBrowser) {
  document.documentElement.classList.add('foo');
}
```

Exempel: Rendera bara en komponent på klientsidan**

Komponenten kan innehålla funktionalitet som inte fungerar på serversidan och då kan man välja att inte rendera komponenten överhuvudtaget.

```svelte
<script>
  import { isBrowser } from '@soleil-se/app-util';
  import ClientSideComponent from './ClientSideComponent.svelte';
</script>


{#if isBrowser}
  <ClientSideComponent />
{/if}
```

När koden byggs ihop för servern kommer dead code elimination i Rollup plocka bort den kod som inte ska köras på serversidan.

#### Fördelar

* Renderas först på servern och visas därför direkt när sidan laddas.
* Renderas när JavaScript är avstängt.
* Indexeras i Sitevisions sök.
* Bättre för SEO.

#### Nackdelar

* Kan bli krångligt att hålla isär vad som körs på servern och på klienten.
* Om man har en app med mycket interaktivitet kan det kosta mer än det smakar.

### SSR

Används när man bygger statiska moduler utan behov av klientsideskript, exempelvis puffar, enklare listningar, banners osv.\
Förfarandet blir ungefär lika som med en universell app men att man utelämnar `main.js`.

> Server-side rendering (SSR) is the generation of the page contents on the server. SSR is generally preferred for SEO. While some search engines can index content that is dynamically generated on the client-side it may take longer even in these cases. It also tends to improve perceived performance and makes your app accessible to users if JavaScript fails or is disabled (which happens [more often than you probably think](https://kryogenix.org/code/browser/everyonehasjs.html)).

index.js**

Går att använda både `render` och `renderServer`. Skillnaden är att `render` kommer läsa in `main.js` om denna finns med i appen. Om man har en SSR app så har man oftast inte det. Annars kan man använda `renderServer` för att försäkra sig om att appen bara renderas på servern.

```js
import router from '@sitevision/api/common/router';
import { render } from '@soleil-se/app-util/server/svelte/5';


import App from './App.svelte';


router.get('/', (req, res) => {
  const props = { foo: 'bar' };
  const html = render(App, props);
  res.send(html);
});
```

[Läs mer om render i index.js](/packages/app-util/svelte/#indexjs).

#### Fördelar

* Renderas när JavaScript är avstängt.
* Indexeras i Sitevisions sök.
* Bättre för SEO.
* Kan användas för exempelvis templates för mail osv.

#### Nackdelar

* Går inte att använda om man behöver ha någon form av interaktivitet i appen.

### CSR

Används när man bygger moduler med mycket interaktivitet som inte behöver fungera med JavaScript avstängt.

> Client-side rendering (CSR) is the generation of the page contents in the web browser using JavaScript. A single-page app (SPA) is an application in which all requests to the server load a single HTML file which then does client-side rendering of the requested contents based on the requested URL.
>
> All navigation is handled on the client-side in a process called client-side routing with per-page contents being updated and common layout elements remaining largely unchanged.
>
> SPAs do not provide SSR, which has the shortcoming described above. However, some applications are not greatly impacted by these shortcomings such as a complex business application behind a login where SEO would not be important > and it is known that users will be accessing the application from a consistent computing environment.

index.js**

Här renderas inget på servern utan appen kommer att renderas på klientsidan, detta görs genom att skicka med en tom sträng i `res.agnosticRender` och de props som ska skickas med till appen.

**index.js**

```js
import router from '@sitevision/api/common/router';


router.get('/', (req, res) => {
  const props = { foo: 'bar' };
  res.agnosticRender('', props);
});
```

main.js**

[Läs mer om render i main.js](/packages/app-util/svelte/#mainjs).

**main.js**

```js
import { render } from '@soleil-se/app-util/client/svelte/5';
import App from './App.svelte';


export default (props, target) => {
  render(App, { props, target });
};
```

#### Fördelar

* Behöver inte tänka på att appen ska fungera på serversidan.

#### Nackdelar

* Går inte att använda om appen ska fungera utan JavaScript påslaget.
* Indexeras inte i Sitevisions sök.

### Hydration

När en applikation först renderas på servern och sedan på klienten används ett koncept som kallas för hydration.

> Svelte components store some state and update the DOM when the state is updated. When fetching data during SSR, by default this data will be transmitted to the client along with the server-rendered HTML. The components can then be initialized on the client with that data without having to call the same API endpoints again. Svelte will then check that the DOM is in the expected state and attach event listeners in a process called hydration. Once the components are fully hydrated, they can react to changes to their properties just like any newly created Svelte component.

## App props

Props som skickas med när appen renderas på servern är tillgängliga i `App.svelte` och genom [getAppProps](/packages/app-util/common#getapppropskey----object).\
Går även att använda [Sveltes context API](https://svelte.dev/docs#setContext), men då är data bara tillgänglig i komponenter och inte js-filer.

Skicka props från servern till appen**

**index.js**

````js
import router from '@sitevision/api/common/router';
import { render } from '@soleil-se/app-util/server/svelte/5';


router.get('/', (req, res) => {
const props = { foo: 'bar' };
res.send(render(props));
});


```svelte
// App.svelte
<script>
  let { foo } = $props();
</script>


<p>{foo}</p>
````

Hämta props djupt i strukturen**

Används oftast om man har data från servern som används långt ner i strukturen så man ska slippa skicka props hela vägen ner.

**App.svelte**

```js
import router from '@sitevision/api/common/router';
import { render } from '@soleil-se/app-util/server/svelte/5';


router.get('/', (req, res) => {
  const props = { foo: 'bar' };
  res.send(render(props));
});
```

**Child.svelte**

```svelte
<script>
  import { getAppProps } from '@soleil-se/app-util';


  const { foo } = getAppProps();
</script>


<p>{foo}</p>
```

## Styling

Stylingen i en Svelte app görs oftast i komponenterna.

Det är inte nödvändigt att använda BEM i en Svelte app då stylingen automatiskt scopas till komponenten.\
<https://svelte.dev/docs/svelte/scoped-styles>

Preprocessors**

Man kan använda antingen SCSS eller Less genom att lägga på `lang` på style taggen.

```svelte
<div>
  Foo
</div>


<style lang="scss">
  @use 'abstracts/variables' as *;


  div {
    background-color: $blue;
  }
</style>
```

### Global styling

Då stylingen är scopad kan man använda `:global` för att nå en selektor globalt.

Global styling**

```svelte
<style>
:global(body) {
  /* this will apply to <body> */
  margin: 0;
}


div :global(strong) {
  /* this will apply to all <strong> elements, in any
    component, that are inside <div> elements belonging
  to this component */
  color: goldenrod;
 }
</style>
```

Med svelte-preprocess går det även att ange global styling på några ytterligare sätt.\
<https://github.com/sveltejs/svelte-preprocess#global-style>

## Konfiguration

För att skapa upp konfiguration för en WebApp kan man använda [@soleil-se/config-svelte](/packages/config-svelte).