# Core Architecture Standards for Svelte
This document outlines the core architectural standards for Svelte projects, focusing on project structure, organization, and fundamental architectural patterns. This guide is intended to promote maintainability, performance, and scalability in Svelte applications.
## 1. Project Structure and Organization
A well-defined project structure is crucial for maintainability and collaboration. Svelte projects benefit from a structure that separates concerns and promotes reusability.
### 1.1 Standard Directory Structure
**Do This:** Adhere to a standardized directory structure that aligns with SvelteKit's conventions and best practices.
**Don't Do This:** Avoid haphazardly placing files and folders without a clear organizational scheme.
**Why:** A consistent structure makes it easier for developers to navigate and understand the project. It also facilitates automated tooling and deployment processes.
"""
├── src/
│ ├── lib/ # Reusable components, utilities, and stores
│ │ ├── components/ # Presentational and container components
│ │ ├── utils/ # Utility functions
│ │ ├── stores/ # Svelte stores for state management
│ │ └── assets/ # Images, SVGs, and other static assets
│ ├── routes/ # SvelteKit routes (pages and API endpoints)
│ │ ├── +page.svelte # Page component
│ │ ├── +page.ts # Page data loading logic
│ │ ├── +server.ts # API endpoints
│ │ └── components/ # Route-specific components
│ ├── app.html # Root HTML template
│ ├── app.d.ts # TypeScript definitions
│ └── hooks.server.ts # Server-side hooks
├── static/ # Static assets (images, fonts, etc.)
├── svelte.config.js # Svelte configuration
├── vite.config.ts # Vite configuration
├── package.json # Project dependencies and scripts
├── README.md # Project documentation
└── tsconfig.json # TypeScript configuration
"""
* **"src/lib":** Contains reusable components, utilities, and Svelte stores. This directory should be further organized by functionality.
* **"src/routes":** Holds SvelteKit routes, where each directory represents a route. "+page.svelte" files are the Svelte components for each page, and "+page.ts" files are used for loading data into the page. "+server.ts" defines API endpoints.
* **"static":** Contains static assets like images and fonts. These are served directly.
* **"svelte.config.js":** SvelteKit configuration file. Includes adapters, preprocessors, and other Svelte-specific settings.
* **"vite.config.ts":** Vite configuration file. Controls the build process, including plugins and optimizations.
### 1.2 Component Organization within "lib"
**Do This:** Organize components within the "src/lib/components" directory into logical groupings based on feature or domain. Create subdirectories for related components.
**Don't Do This:** Dump all components into a single "components" directory.
**Why:** Grouping components makes it easier to locate and understand the purpose of each component.
"""
src/lib/components/
├── UserProfile/
│ ├── UserProfileCard.svelte
│ ├── UserProfileEditor.svelte
│ └── UserAvatar.svelte
├── ProductCard.svelte
└── Button.svelte
"""
**Example:**
"""svelte
{user.name}
<p>{user.bio}</p>
"""
### 1.3 Route-Specific Components
**Do This:** When a component is only used within a specific route, place it in a "components" subdirectory within the route directory.
**Don't Do This:** Place route-specific components in the global "src/lib/components" directory.
**Why:** This improves encapsulation and reduces the global namespace of components, leading to better maintainability.
"""
src/routes/
└── blog/
├── [slug]/
│ ├── +page.svelte
│ └── components/
│ └── BlogPostContent.svelte
"""
### 1.4 Utility Functions
**Do This:** Place reusable utility functions in the "src/lib/utils" directory. Group related functions into separate modules (e.g., "date.ts", "string.ts").
**Don't Do This:** Scatter utility functions throughout the codebase or duplicate them in multiple places.
**Why:** Centralized utility functions promote code reuse and reduce redundancy.
"""typescript
// src/lib/utils/date.ts
export function formatDate(date: Date): string {
return new Intl.DateTimeFormat('en-US').format(date);
}
"""
### 1.5 Svelte Stores
**Do This:** Centralize state management using Svelte stores in the "src/lib/stores" directory. Use separate files for different domains of application state (e.g., "user.ts", "settings.ts").
**Don't Do This:** Manage state in a haphazard way or rely heavily on prop drilling.
**Why:** Svelte stores provide a reactive and efficient way to manage application state.
"""typescript
// src/lib/stores/user.ts
import { writable } from 'svelte/store';
export const user = writable({
id: null,
name: '',
email: ''
});
export function setUser(userData) {
user.set(userData);
}
export function clearUser() {
user.set({ id: null, name: '', email: '' });
}
"""
## 2. Architectural Patterns
Svelte applications can benefit from well-established architectural patterns to improve organization, testability, and scalability.
### 2.1 Component-Based Architecture
**Do This:** Embrace a component-based architecture, breaking down the user interface into reusable components.
**Don't Do This:** Create monolithic components that handle too much logic and rendering.
**Why:** Component-based architecture promotes code reuse, testability, and maintainability. Svelte's core design encourages this naturally.
"""svelte
{label}
"""
### 2.2 Container/Presentational Component Pattern
**Do This:** Separate components into "container" (or "smart") components that handle data fetching and logic, and "presentational" (or "dumb") components that focus on rendering UI.
**Don't Do This:** Mix data fetching and complex logic directly within presentational components.
**Why:** This separation makes it easier to test and reuse components. Container components can be responsible for connecting to stores or APIs, while presentational components focus on displaying data.
"""svelte
{#each products as product (product.id)}
{product.name} - ${product.price}
{/each}
"""
### 2.3 Actions for DOM Manipulation
**Do This:** Use Svelte actions for DOM manipulation and side effects related to specific elements.
**Don't Do This:** Directly manipulate the DOM within component logic.
**Why:** Actions provide a clean and declarative way to interact with DOM elements.
"""svelte
export function tooltip(node: HTMLElement, text: string) {
let tooltipElement: HTMLDivElement;
function createTooltip() {
tooltipElement = document.createElement('div');
tooltipElement.textContent = text;
tooltipElement.style.position = 'absolute';
tooltipElement.style.background = 'black';
tooltipElement.style.color = 'white';
tooltipElement.style.padding = '5px';
document.body.appendChild(tooltipElement);
}
function destroyTooltip() {
if (tooltipElement) {
tooltipElement.remove();
tooltipElement = null;
}
}
function updateTooltipPosition(event: MouseEvent) {
if (tooltipElement) {
tooltipElement.style.top = "${event.pageY + 10}px";
tooltipElement.style.left = "${event.pageX + 10}px";
}
}
node.addEventListener('mouseover', createTooltip);
node.addEventListener('mousemove', updateTooltipPosition);
node.addEventListener('mouseout', destroyTooltip);
return {
update(newText: string) {
text = newText;
if (tooltipElement) {
tooltipElement.textContent = text;
}
},
destroy() {
node.removeEventListener('mouseover', createTooltip);
node.removeEventListener('mousemove', updateTooltipPosition);
node.removeEventListener('mouseout', destroyTooltip);
destroyTooltip();
}
};
}
"""
"""svelte
Hover me
"""
### 2.4 Form Handling with Actions
**Do This:** Employ Svelte actions for managing form state and validation instead of directly binding to input elements. This promotes cleaner, more reusable, and testable form logic. Libraries like "felte" are an excellent choice.
**Don't Do This:** Use inline event handlers and manually manage input values within your components.
**Why:** Using actions and form helper libraries encapsulates form logic and improves the overall architecture of forms.
"""bash
npm install @felte/core @felte/validator-zod zod
"""
"""svelte
First Name
{#if $errors.firstName}
{$errors.firstName}
{/if}
Last Name
{#if $errors.lastName}
{$errors.lastName}
{/if}
{#if $errors.email}
{$errors.email}
{/if}
Submit
"""
## 3. State Management
Efficient state management is essential for complex Svelte applications.
### 3.1 Choosing the Right State Management Approach
**Do This:** Evaluate the scale of state complexity required for each project when choosing a state management solution. Choose Svelte stores for local component state and simple application-wide state. Consider more robust solutions, like Zustand or Redux, for complex scenarios. Pinia is a great choice if using Vue previously as it mimics the API.
**Don't Do This:** Over-engineer state management in small projects or under-engineer it in large projects.
**Why:** Appropriate state management prevents performance problems, improves maintainability, and enhances code readability.
### 3.2 Global State with Svelte Stores
**Do This:** Use Svelte stores for managing global application state. Leverage derived stores for computed values and read-only stores for immutable data.
**Don't Do This:** Mutate store values directly without using the "set", "update", or "subscribe" methods.
**Why:** Stores provide a reactive and predictable way to manage application state. Using the built-in methods ensures that components are properly updated when the state changes.
"""typescript
// src/lib/stores/cart.ts
import { writable, derived } from 'svelte/store';
export const cartItems = writable([]);
export const totalItems = derived(cartItems, ($cartItems) => $cartItems.length);
export const totalPrice = derived(cartItems, ($cartItems) => {
return $cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
});
"""
"""svelte
<p>Total items: {$totalItems}</p>
<p>Total price: {$totalPrice}</p>
"""
### 3.3 Context API for Component-Specific State
**Do This:** Use the Svelte context API ("setContext", "getContext") to share state and functionality between related components in a localized scope (such as within a specific route or component tree).
**Don't Do This:** Overuse the context API for global state management. Svelte stores are generally better for that purpose.
**Why:** The context API provides a way to avoid prop drilling and pass data down the component tree without explicitly passing props through each intermediate component.
"""svelte
"""
"""svelte
Current theme: {$theme}
Toggle Theme
"""
## 4. API Communication
Consider best practices for fetching data and interacting with APIs.
### 4.1 SvelteKit Load Functions
**Do This:** Use SvelteKit's "load" functions ("+page.ts", "+layout.ts") to fetch data for routes and layouts. This allows for server-side rendering and improved SEO.
**Don't Do This:** Fetch data directly in components using "onMount" unless it is client-side specific.
**Why:** "load" functions execute on the server during SSR and can pre-render the page with data, improving performance and SEO.
"""typescript
// src/routes/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch }) => {
const response = await fetch('/api/products');
const products = await response.json();
return {
products
};
};
"""
"""svelte
{#each products as product (product.id)}
{product.name} - ${product.price}
{/each}
"""
### 4.2 API Endpoints with SvelteKit
**Do This:** Create API endpoints using SvelteKit's server routes ("+server.ts"). Follow RESTful principles for endpoint design.
**Don't Do This:** Implement complex business logic directly within components.
**Why:** Server routes allow you to create clean, maintainable APIs that can be used by your Svelte application and other clients.
"""typescript
// src/routes/api/products/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
const products = [
{ id: 1, name: 'Product A', price: 20 },
{ id: 2, name: 'Product B', price: 30 }
];
export const GET: RequestHandler = async () => {
return json(products);
};
"""
### 4.3 Error Handling
**Do This:** Implement robust error handling for API requests. Use "try...catch" blocks and provide informative error messages to the user.
**Don't Do This:** Ignore potential errors or display generic error messages.
**Why:** Proper error handling improves the user experience and helps debug issues.
"""typescript
// Example within a SvelteKit load function
export const load: PageLoad = async ({ fetch }) => {
try {
const response = await fetch('/api/products');
if (!response.ok) {
throw new Error("Failed to fetch products: ${response.status}");
}
const products = await response.json();
return {
products
};
} catch (error) {
console.error(error);
return {
status: 500,
error: new Error('Could not load products')
};
}
};
"""
## 5. Naming Conventions
Consistent naming conventions improve code readability and maintainability.
### 5.1 Components
**Do This:** Use PascalCase for component names (e.g., "UserProfileCard.svelte").
**Don't Do This:** Use camelCase or snake_case for component names.
**Why:** PascalCase is the standard convention for components in most UI frameworks.
### 5.2 Variables and Functions
**Do This:** Use camelCase for variables and functions (e.g., "userName", "formatDate").
**Don't Do This:** Use PascalCase or snake_case for variables and functions.
**Why:** camelCase is the standard convention for variables and functions in JavaScript and TypeScript.
### 5.3 Files and Directories
**Do This:** Use kebab-case for file and directory names (e.g., "user-profile.svelte", "api-utils").
**Don't Do This:** Use camelCase or snake_case for file and directory names.
**Why:** kebab-case is a common convention for file and directory names in web development.
### 5.4 Svelte Stores
**Do This:** Use camelCase for store names (e.g., "user", "settings"). Consider adding a "$" prefix when referencing the store's value in the template (e.g. "$user.name").
**Don't Do This:** Use PascalCase or snake_case for store names.
**Why:** camelCase aligns with JavaScript naming conventions, and the "$" prefix clearly indicates that you're accessing a reactive value.
## 6. TypeScript Usage
Leverage TypeScript to improve code quality and maintainability.
### 6.1 Strict Typing
**Do This:** Enable strict mode in your "tsconfig.json" file (""strict": true").
**Don't Do This:** Disable strict mode or use "any" type excessively.
**Why:** Strict typing helps catch errors early and improves code reliability.
### 6.2 Explicit Types
**Do This:** Provide explicit types for component props, store values, and function parameters.
**Don't Do This:** Rely solely on type inference, especially for complex types.
**Why:** Explicit types improve code readability and prevent unexpected type errors.
"""typescript
// src/lib/components/Button.svelte
{label}
"""
### 6.3 Interfaces and Types
**Do This:** Define interfaces or types for data structures used in your application.
**Don't Do This:** Use inline type definitions or anonymous types.
**Why:** Interfaces and types provide a clear and reusable way to define data structures.
"""typescript
// src/lib/types/user.ts
export interface User {
id: number;
name: string;
email: string;
}
"""
"""typescript
// Usage in a store:
import { writable } from 'svelte/store';
import type { User } from '$lib/types/user';
export const user = writable(null);
"""
By adhering to these core architectural standards, Svelte developers can create maintainable, performant, and scalable applications. This document is a living guide and should be updated as new best practices and features emerge in the Svelte ecosystem.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Testing Methodologies Standards for Svelte This document outlines the recommended testing methodologies and best practices for Svelte applications. Effective testing ensures application reliability, maintainability, and performance. It covers unit, integration, and end-to-end testing with Svelte-specific examples and considerations. ## 1. General Testing Principles * **Do This:** Adhere to the testing pyramid: a large base of unit tests, a smaller layer of integration tests, and an even smaller peak of end-to-end tests. * **Don't Do This:** Rely solely on end-to-end tests. They are slower, more brittle, and provide less specific feedback than unit or integration tests. * **Why:** The testing pyramid promotes a balanced testing strategy that maximizes coverage and minimizes testing costs. * **Code Example (Pyramid Visualization - Conceptual):** """ # Unit Tests: Many, Fast, Isolated # Integration Tests: Fewer, Slower, Interacting Units # E2E Tests: Fewest, Slowest, Full Application """ ## 2. Unit Testing ### 2.1. Purpose and Scope * **Do This:** Unit test individual components, functions, and modules in isolation. Focus on validating the logic within each unit. Aim for high code coverage for critical components. * **Don't Do This:** Neglect unit testing in favor of integration or end-to-end tests. This makes isolating bugs significantly harder. * **Why:** Unit tests verify the behavior of individual units of code, making it easier to identify and fix bugs early in the development process. ### 2.2. Tools and Libraries * **Do This:** Use Jest or Vitest as a test runner and assertion library. Consider using "@testing-library/svelte" (or "@testing-library/dom" directly) for rendering and interacting with components in a user-centric way. * **Don't Do This:** Use assertion libraries that are not well-maintained or lack Svelte-specific utilities. * **Why:** Jest and Vitest provide excellent performance, mocking capabilities, and broad ecosystem support. "@testing-library/svelte" promotes testing components as a user would interact with them. ### 2.3. Writing Effective Unit Tests * **Do This:** Write tests that are readable, maintainable, and focused on a single concern. Use descriptive test names that clearly communicate the expected behavior. Utilize "describe" blocks to organize tests. * **Don't Do This:** Write overly complex or brittle tests that are difficult to understand or maintain. Avoid testing implementation details that are likely to change. Don't assert vague or overly broad outcomes. * **Why:** Readable and maintainable tests improve the overall quality of the codebase and make it easier to refactor and add new features. Testing behavior (what the user sees/experiences) rather than implementation leads to more robust and less fragile tests. ### 2.4. Unit Testing Examples #### 2.4.1. Testing a Simple Component """svelte <!-- Counter.svelte --> <script> let count = 0; function increment() { count += 1; } </script> <button on:click={increment}> Count: {count} </button> """ """javascript // Counter.spec.js (using Vitest and @testing-library/svelte) import { render, fireEvent } from '@testing-library/svelte'; import Counter from './Counter.svelte'; import { describe, it, expect } from 'vitest'; describe('Counter Component', () => { it('should render the initial count', () => { const { getByText } = render(Counter); expect(getByText('Count: 0')).toBeInTheDocument(); }); it('should increment the count when the button is clicked', async () => { const { getByText } = render(Counter); const button = getByText('Count: 0'); await fireEvent.click(button); expect(getByText('Count: 1')).toBeInTheDocument(); }); }); """ #### 2.4.2. Testing Component Properties """svelte <!-- Greeting.svelte --> <script> export let name; </script> <h1>Hello, {name}!</h1> """ """javascript // Greeting.spec.js import { render } from '@testing-library/svelte'; import Greeting from './Greeting.svelte'; import { describe, it, expect } from 'vitest'; describe('Greeting Component', () => { it('should render the greeting with the provided name', () => { const { getByText } = render(Greeting, { props: { name: 'World' } }); expect(getByText('Hello, World!')).toBeInTheDocument(); }); it('should render the greeting with a different name', () => { const { getByText } = render(Greeting, { props: { name: 'Svelte' } }); expect(getByText('Hello, Svelte!')).toBeInTheDocument(); }); }); """ #### 2.4.3. Testing Events """svelte <!-- Button.svelte --> <script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); function handleClick() { dispatch('click', { text: 'Button Clicked!' }); } </script> <button on:click={handleClick}>Click Me</button> """ """javascript // Button.spec.js import { render, fireEvent } from '@testing-library/svelte'; import Button from './Button.svelte'; import { describe, it, expect, vi } from 'vitest'; describe('Button Component', () => { it('should dispatch a click event when the button is clicked', async () => { const { getByText, component } = render(Button); const button = getByText('Click Me'); const mockFn = vi.fn(); component.$on('click', mockFn); // Use component.$on for listening await fireEvent.click(button); expect(mockFn).toHaveBeenCalledTimes(1); expect(mockFn).toHaveBeenCalledWith(expect.objectContaining({detail: { text: 'Button Clicked!'}})); }); }); """ ### 2.5. Mocking Dependencies * **Do This:** Use mocking libraries like Jest's "jest.mock()" or Vitest's "vi.mock()" to isolate the component being tested from its dependencies. Mock external APIs, stores, and other components. * **Don't Do This:** Mock indiscriminately. Only mock dependencies that are not relevant to the unit being tested. Avoid mocking implementation details. * **Why:** Mocking allows you to control the behavior of dependencies, ensuring that the unit being tested is isolated and that tests are deterministic. * **Code Example:** """javascript // api.js (Example API Module) export async function fetchData() { const response = await fetch('/api/data'); return response.json(); } """ """svelte <!-- MyComponent.svelte --> <script> import { fetchData } from './api.js'; let data = null; async function loadData() { data = await fetchData(); } loadData(); </script> {#if data} <p>Data: {data.value}</p> {:else} <p>Loading...</p> {/if} """ """javascript // MyComponent.spec.js import { render, waitFor, screen } from '@testing-library/svelte'; import MyComponent from './MyComponent.svelte'; import * as api from './api.js'; // Import the module import { describe, it, expect, vi } from 'vitest'; describe('MyComponent', () => { it('should render data fetched from the API', async () => { const mockFetchData = vi.spyOn(api, 'fetchData'); mockFetchData.mockResolvedValue({ value: 'Mocked Data' }); render(MyComponent); await waitFor(() => { expect(screen.getByText('Data: Mocked Data')).toBeInTheDocument(); }); mockFetchData.mockRestore(); // Restore the original function. Important for clean tests }); }); """ ### 2.6. Testing Stores * **Do This:** Test the behavior of stores independently from components that use them. Use "get()" from "svelte/store" to synchronously access the store's value during tests. Ensure async updates to stores are handled correctly in tests (using "await tick()" where necessary within components, but primarily using "await" with the store updates directly within the test). * **Don't Do This:** Test stores implicitly through component tests. * **Why:** Testing stores directly ensures their core logic is correct before being used within components. * **Code Example:** """javascript // myStore.js import { writable } from 'svelte/store'; export const count = writable(0); export function increment() { count.update(n => n + 1); } export function decrement() { count.update(n => n - 1); } """ """javascript // myStore.spec.js import { get } from 'svelte/store'; import { count, increment, decrement } from './myStore'; import { describe, it, expect } from 'vitest'; describe('myStore', () => { it('should initialize with a count of 0', () => { expect(get(count)).toBe(0); }); it('should increment the count', () => { increment(); expect(get(count)).toBe(1); }); it('should decrement the count', () => { decrement(); expect(get(count)).toBe(0); }); }); """ ## 3. Integration Testing ### 3.1. Purpose and Scope * **Do This:** Integration test how multiple components and units of code work together. Focus on verifying interactions between different parts of the application. * **Don't Do This:** Treat integration tests as replacements for unit tests. Ensure to test units individually before testing them in conjunction. * **Why:** Integration tests expose issues that arise when different parts of the application interact, such as incorrect data flow or unexpected side effects. ### 3.2. Testing Strategies * **Do This:** Use a combination of top-down and bottom-up integration testing approaches. Focus on major user flows and critical application features. * **Don't Do This:** Avoid testing all possible combinations of interactions. Prioritize testing the most common and important scenarios. * **Why:** A balanced approach to integration testing provides comprehensive coverage without becoming overwhelming. ### 3.3. Integration Testing Examples #### 3.3.1. Testing Component Communication """svelte <!-- Parent.svelte --> <script> import Child from './Child.svelte'; let messageFromChild = ''; function handleMessage(event) { messageFromChild = event.detail.message; } </script> <Child on:message={handleMessage} /> <p>Message from Child: {messageFromChild}</p> """ """svelte <!-- Child.svelte --> <script> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); function sendMessage() { dispatch('message', { message: 'Hello from Child!' }); } </script> <button on:click={sendMessage}>Send Message</button> """ """javascript // Parent.spec.js import { render, fireEvent, screen } from '@testing-library/svelte'; import Parent from './Parent.svelte'; import { describe, it, expect } from 'vitest'; describe('Parent Component Integration', () => { it('should receive and display a message from the child component', async () => { render(Parent); const button = screen.getByText('Send Message'); await fireEvent.click(button); expect(screen.getByText('Message from Child: Hello from Child!')).toBeInTheDocument(); }); }); """ #### 3.3.2. Testing Store Interactions """svelte <!-- CounterComponent.svelte --> <script> import { count, increment, decrement } from './myStore'; import { get } from 'svelte/store'; </script> <button on:click={increment}>Increment</button> <button on:click={decrement}>Decrement</button> <p>Count: {$count}</p> """ """javascript // CounterComponent.spec.js import { render, fireEvent, screen } from '@testing-library/svelte'; import CounterComponent from './CounterComponent.svelte'; import { increment, decrement, count } from './myStore'; import { describe, it, expect} from 'vitest'; import { get } from 'svelte/store'; // Ensure store is reset before each test. Vitest runs tests in parallel, and stores are global! import { beforeEach } from 'vitest'; beforeEach(() => { count.set(0); }); describe('CounterComponent with Store Integration', () => { it('should increment and decrement the count using the store', async () => { render(CounterComponent); const incrementButton = screen.getByText('Increment'); const decrementButton = screen.getByText('Decrement'); await fireEvent.click(incrementButton); expect(screen.getByText('Count: 1')).toBeInTheDocument(); await fireEvent.click(decrementButton); expect(screen.getByText('Count: 0')).toBeInTheDocument(); }); it('should update the store directly and reflect in the component', async () => { render(CounterComponent); increment(); expect(screen.getByText('Count: 1')).toBeInTheDocument(); }); }); """ ## 4. End-to-End (E2E) Testing ### 4.1. Purpose and Scope * **Do This:** Use E2E tests to verify the entire application flow, from user interaction to data persistence, across different browsers and environments. Focus on critical user journeys. * **Don't Do This:** Rely on E2E tests as the primary testing method. They are slow, expensive, and provide less specific feedback than unit or integration tests. * **Why:** E2E tests provide confidence that the application works as expected from the user's perspective, including interactions with external systems. ### 4.2. Tools and Libraries * **Do This:** Use Playwright or Cypress for writing and running E2E tests. These tools provide excellent browser automation capabilities, automatic waiting, and debugging features. Consider using Docker for consistent test environments. * **Don't Do This:** Use outdated or unmaintained E2E testing tools. * **Why:** Playwright and Cypress offer modern features that simplify E2E testing and improve reliability. ### 4.3. Writing Effective E2E Tests * **Do This:** Write tests that are resilient to UI changes. Use data attributes or role-based selectors to locate elements. Avoid relying on CSS classes or text content that may change. Use "getByRole" and "findByRole" from Testing Library within Playwright/Cypress where possible. * **Don't Do This:** Write brittle tests that are easily broken by minor UI changes. This leads to test fatigue and reduces the value of E2E testing. Don't include sleeps or hardcoded waits if possible; leverage the automatic waiting features of Playwright/Cypress. * **Why:** Resilient tests reduce maintenance costs and provide more reliable feedback. ### 4.4. E2E Testing Example (Playwright) """javascript // playwright.config.js import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', fullyParallel: true, reporter: 'html', use: { baseURL: 'http://localhost:3000', // Replace with your app's URL trace: 'on-first-retry', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, // Add other browsers as needed ], webServer: { command: 'npm run dev', // Replace with your dev server command url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, }); """ """javascript // tests/example.spec.js import { test, expect } from '@playwright/test'; test('has title', async ({ page }) => { await page.goto('/'); await expect(page).toHaveTitle("My Svelte App"); // Replace with your app's title }); test('counter increments correctly', async ({ page }) => { await page.goto('/'); const incrementButton = await page.getByRole('button', {name: 'Increment'}); const countText = page.locator('text=Count:'); // Use a CSS or text locator if roles aren't applicable await expect(countText).toContainText('Count: 0'); await incrementButton.click(); await expect(countText).toContainText('Count: 1'); }); """ ### 4.5. E2E Testing Considerations for Svelte * **Testing Transitions/Animations:** Playwright and Cypress can handle waiting for animations and transitions to complete before making assertions. Use "waitFor" or "waitForSelector" with appropriate timeouts to ensure elements are fully rendered. * **Shadow DOM:** If using web components extensively, understand how to query elements within the shadow DOM using Playwright or Cypress APIs. * **SvelteKit Specifics:** When testing SvelteKit apps, ensure the development server is running before running tests and that the correct base URL is configured. ## 5. Accessibility Testing * **Do This:** Integrate accessibility testing into your workflow. Use tools like axe DevTools, Lighthouse, or pa11y to identify accessibility issues. Write tests that verify that components are accessible to users with disabilities. Use semantic HTML. Employ ARIA attributes where necessary, testing them for correctness. * **Don't Do This:** Ignore accessibility testing. This can exclude users with disabilities and violate accessibility guidelines. * **Why:** Accessibility testing ensures that the application is usable by everyone, regardless of their abilities. * **Code Example (using axe DevTools):** """javascript // Accessibility.spec.js (Playwright) import { test, expect } from '@playwright/test'; import { AxeBuilder } from '@axe-core/playwright'; test.describe('Accessibility Tests', () => { test('homepage should not have any automatically detectable accessibility issues', async ({ page }) => { await page.goto('/'); const axeBuilder = new AxeBuilder({ page }); const accessibilityScanResults = await axeBuilder.analyze(); expect(accessibilityScanResults.violations).toEqual([]); }); }); """ ## 6. Performance Testing * **Do This:** Use performance testing tools like Lighthouse or WebPageTest to measure the performance of the application. Write tests that verify that components render quickly and efficiently. Profile components with Svelte's "$debug" and browser devtools to identify performance bottlenecks. * **Don't Do This:** Neglect performance testing. This can lead to slow and unresponsive applications that provide a poor user experience. * **Why:** Performance testing ensures that the application meets performance requirements and provides a smooth user experience. ## 7. Continuous Integration (CI) * **Do This:** Integrate all testing types into a CI/CD pipeline. Run unit tests on every commit. Run integration and accessibility tests regularly. Schedule E2E tests to run nightly or weekly. * **Don't Do This:** Manually run tests. CI/CD automates testing and reduces the risk of human error. * **Why:** CI/CD automates the testing process, ensuring that code changes are thoroughly tested before being deployed. ## 8. Code Coverage * **Do This:** Aim for high code coverage, particularly for core components and critical functionality. Use code coverage tools to identify untestable or untested code. * **Don't Do This:** Use code coverage as the *sole* measure of test quality. High coverage does not guarantee that tests are effective or that the application is bug-free. * **Why:** Code coverage provides a metric for assessing the extent to which the codebase is tested. ## 9. Svelte 5 Testing Considerations * **Reactivity Changes:** Be mindful of the reactivity changes in Svelte 5 (using runes). Ensure tests accurately reflect the new reactivity system, particularly when testing component updates and store interactions. Pay extra attention to testing derived state and how it updates based on reactive dependencies. * **.svelte.js/.ts Files:** Recognize that tests might now involve importing and testing ".svelte.js" or ".svelte.ts" files directly, which contain logic previously confined to the "<script>" tag within ".svelte" components. By following these testing methodologies and best practices, you can build robust, maintainable, and performant Svelte applications. This leads to higher quality software and a better experience for both developers and users.
# Deployment and DevOps Standards for Svelte This document outlines the coding standards for deployment and DevOps practices specific to Svelte applications. Adhering to these standards will help ensure consistent, maintainable, performant, and secure deployments. These standards are based on the latest version of Svelte and its ecosystem. ## 1. Build Processes and CI/CD ### 1.1. Standardizing Build Processes **Do This:** * Use a consistent build tool and configuration across all environments (development, staging, production). SvelteKit projects should use the "svelte-package" and Vite configuration. * Define build scripts in "package.json" using standard commands like "build", "preview", and "check". * Utilize environment variables properly for configuration management. **Don't Do This:** * Don't rely on manual build steps or environment-specific build scripts. * Don't hardcode sensitive information (API keys, secrets) directly in the code or build scripts. **Why:** Consistent build processes ensure reproducibility and prevent environment-specific issues. Using environment variables allows for flexible configuration without modifying the code. **Example:** """json // package.json { "scripts": { "dev": "vite dev", "build": "vite build", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "lint": "prettier --plugin-search-dir . --check . && eslint .", "format": "prettier --plugin-search-dir . --write ." }, "devDependencies": { "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/kit": "^1.5.0", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", "eslint": "^8.28.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-svelte3": "^4.0.0", "prettier": "^2.8.0", "prettier-plugin-svelte": "^2.8.1", "svelte": "^3.54.0", "svelte-check": "^3.0.1", "tslib": "^2.4.1", "typescript": "^5.0.0", "vite": "^4.0.0" } } """ ### 1.2. CI/CD Pipelines **Do This:** * Implement a CI/CD pipeline using tools like GitHub Actions, GitLab CI, or Jenkins. * Automate build, test, and deployment processes. * Include linting, code formatting, and static code analysis in the pipeline. * Use automated testing (unit, integration, end-to-end) to ensure code quality. * Cache dependencies to speed up build times. * Implement version control tagging for releases. * Use SvelteKit's adapters for different deployment targets (Node, Vercel, Netlify, static). **Don't Do This:** * Don't deploy directly from developer machines. * Don't skip automated testing in the pipeline. * Don't expose sensitive data in CI/CD logs. **Why:** CI/CD pipelines automate the deployment process, reduce errors, and improve code quality. **Example (GitHub Actions):** """yaml # .github/workflows/deploy.yml name: Deploy to Production on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' # Or the version your project targets cache: 'npm' - name: Install dependencies run: npm install - name: Lint and Format run: npm run lint && npm run format - name: Run Tests run: npm run test:unit # or your test command - name: Build run: npm run build - name: Deploy to Vercel # Or other deployment target like Netlify run: vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }} env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} """ ### 1.3 Adapters **Do This:** * Choose an appropriate adapter for your deployment environment in "svelte.config.js". SvelteKit provides official adapters for Node.js, Vercel, Netlify, Cloudflare Pages, amongst many others. Community adapters are also available. * Configure the adapter correctly based on your environment needs (e.g., static adapter settings for SPA deployment). * Use environment variables to configure the adapter at build time. **Don't Do This:** * Ignore the adapter configuration. Incorrect adapter or options can make your application not function as expected. * Commit adapter configuration variables (like API keys) to your repository. **Why:** Adapters adapt SvelteKit applications to different server environments. Configuring the adapter is crucial for optimizing the appliclation for that server environment (or edge runtime, or static hoster). **Example ("svelte.config.js" with Vercel adapter):** """javascript // svelte.config.js import adapter from '@sveltejs/adapter-vercel'; import { vitePreprocess } from 'svelte-preprocess'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { adapter: adapter({ // Options for the adapter can be added here. }), }, preprocess: [vitePreprocess()] }; export default config; """ ## 2. Production Considerations ### 2.1. Environment Variables **Do This:** * Store all configuration parameters as environment variables. * Use ".env" files (with tooling like "dotenv") for local development, but NEVER commit ".env" files to version control. * Set environment variables in the deployment environment (e.g., Vercel, Netlify). **Don't Do This:** * Don't hardcode configuration parameters in the source code. * Don't commit API keys or secrets to version control. * Avoid using "process.env" directly in components. Create separate configuration modules for improved testability. **Why:** Using environment variables separates configuration from code, making it easier to manage deployments across different environments and improving security. **Example:** """javascript // src/lib/config.js export const API_URL = process.env.API_URL || 'http://localhost:3000/api'; // Default for development export const APP_NAME = process.env.APP_NAME || 'My Svelte App'; """ ### 2.2. Security **Do This:** * Use HTTPS for all production traffic. Enforce HTTPS redirects at your hosting provider. * Sanitize all user inputs to prevent XSS vulnerabilities. Use Svelte's templating engine for automatic escaping. * Implement proper authentication and authorization mechanisms. Use established libraries or services for authentication. * Implement rate limiting to protect against brute-force attacks. * Regularly update dependencies to patch security vulnerabilities. Use tools like "npm audit" to identify vulnerable packages. **Don't Do This:** * Don't store sensitive data (passwords, API keys) in local storage or cookies. * Don't disable CSP (Content Security Policy) without a strong understanding of the implications. * Don't rely on client-side validation alone for security. Always validate data on the server. **Why:** Security vulnerabilities can compromise user data and application integrity. Addressing these issues early in development is essential. Svelte's reactivity and component model can help with consistent sanitization if used correctly. **Example (Server-side validation using SvelteKit endpoints):** """typescript // src/routes/api/submit/+server.ts import { json, type RequestHandler } from '@sveltejs/kit'; export const POST: RequestHandler = async ({ request }) => { const data = await request.json(); // Sanitize and validate input const email = data.email?.trim(); if (!email || !email.includes('@')) { return json({ error: 'Invalid email' }, { status: 400 }); } // Process the data if valid // ... return json({ success: true }); }; """ ### 2.3. Performance **Do This:** * Optimize images using tools like ImageOptim or "imagemin". Use responsive images with "<picture>" or "srcset" attributes. Consider using Svelte's "<img>" tag enhancements. * Use code splitting to reduce initial load time by splitting the app into smaller chunks, which you can configure in Vite. * Lazy-load non-critical components and images. * Enable browser caching for static assets. Configure cache headers appropriately. * Monitor application performance using tools like Google PageSpeed Insights, web.dev, or browser developer tools. * Use Svelte's built-in reactivity efficiently. Avoid unnecessary computations in reactive blocks. * Leverage server-side rendering (SSR) or static site generation (SSG) where appropriate for improved initial load time and SEO. **Don't Do This:** * Don't load large, unoptimized images. * Don't block the main thread with long-running JavaScript tasks. * Don't make excessive network requests. * Don't over-rely on javascript when SSR options for Svelte or SvelteKit will do. **Why:** Performance is critical for user experience. Optimizing assets and code can significantly improve load times and reduce resource consumption. **Example (Lazy loading a component):** """svelte {#if showComponent} <MyComponent /> {:else} <button on:click={() => showComponent = true}>Load Component</button> {/if} <script> let showComponent = false; import MyComponent from './MyComponent.svelte'; </script> """ ### 2.4. Logging and Monitoring **Do This:** * Implement centralized logging using tools like Winston, Bunyan, or a dedicated logging service (e.g., Datadog, Loggly). * Log important events, errors, and warnings. Include contextual information in log messages (e.g., user ID, request ID). * Use monitoring tools (e.g., Prometheus, Grafana, New Relic) to track application health, performance metrics, and error rates. * Set up alerts for critical errors and performance degradation. **Don't Do This:** * Don't log sensitive data (passwords, API keys). * Don't rely solely on console logs for production debugging. * Don't ignore error messages or warnings. Address them promptly. **Why:** Logging and monitoring provide visibility into application behavior, making it easier to identify and resolve issues. **Example (Logging using Winston - server-side):** """javascript // src/hooks.server.js or a dedicated logging module import winston from 'winston'; const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), ], }); export const handle = async ({ event, resolve }) => { try { const response = await resolve(event); return response; } catch (error) { logger.error('Unhandled exception', { url: event.url.pathname, error: error.message, stack: error.stack, }); throw error; // Re-throw to allow SvelteKit error handling } }; """ ### 2.5 SvelteKit Specific Enhancements **Do This:** * Take full advantage of SvelteKit's "handle" hook for request lifecycle management, including authentication, authorization, and data enrichment. * Consider using SvelteKit's serverless functions for API endpoints. * Learn SvelteKit's data loading best practices. Don't create side effects in your component initialization. * Use the "invalidate" function to trigger re-fetches when data changes **Don't Do This:** * Over complicate using SvelteKit "+page.server.js" to fetch data * Forget to use environment variables when creating a "baseURL" **Why:** These features improve developer productivity, performance, and maintainability. **Example:** """typescript // src/routes/+layout.server.ts import { API_URL } from '$lib/config'; export const load = async ({ fetch, cookies, url }) => { const session = cookies.get('sessionid'); try { const response = await fetch("${API_URL}/todos", { headers: { accept: 'application/json', cookie: "sessionid=${session ?? ''}" } }); const todos = await response.json(); return {todos}; } catch(e) { console.log(e) return { todos: [] }; } }; """ ## 3. DevOps Best Practices ### 3.1. Infrastructure as Code (IaC) **Do This:** * Use IaC tools like Terraform, AWS CloudFormation, or Azure Resource Manager to define and manage infrastructure. * Store IaC configurations in version control. * Automate infrastructure deployments using CI/CD pipelines. **Don't Do This:** * Don't manually provision infrastructure. * Don't store infrastructure configurations locally. **Why:** IaC enables repeatable and predictable infrastructure deployments. ### 3.2. Containerization **Do This:** * Package Svelte applications into Docker containers. * Use Docker Compose for local development and testing. * Orchestrate containers using Kubernetes or Docker Swarm for production deployments. **Don't Do This:** * Don't deploy applications directly to bare metal servers. * Don't expose unnecessary ports or volumes in Docker containers. **Why:** Containerization provides consistent and isolated environments for applications. ### 3.3. Monitoring and Alerting **Do This:** * Implement comprehensive monitoring of infrastructure and application resources. * Set up alerts for critical events (e.g., high CPU usage, disk space exhaustion, application errors). * Use a central monitoring dashboard to visualize application health and performance. **Don't Do This:** * Don't ignore monitoring alerts. * Don't rely solely on manual monitoring. **Why:** Monitoring and alerting enable proactive identification and resolution of issues. By consistently applying these deployment and DevOps strategies, development teams can deliver Svelte applications that are robust, scalable, and secure. This will improve deployment confidence and reduce operational costs.
# Performance Optimization Standards for Svelte This document outlines standards for optimizing Svelte applications, focusing on speed, responsiveness, and minimizing resource usage. These guidelines are designed to improve application performance and maintainability and reflect best practices for the latest version of Svelte. ## 1. Component Architecture and Data Management ### 1.1. Minimize Component Updates **Standard:** Only update components when necessary by carefully managing data dependencies and using fine-grained reactivity. **Why:** Excessive component updates lead to unnecessary re-renders, degrading performance. Svelte's reactivity is powerful, but it's crucial to use it judiciously. **Do This:** * Use derived stores ("derived") to compute values based on other stores, preventing unnecessary updates when the source stores haven't changed. * Employ local state variables ("let") for data that doesn't require reactivity across multiple components. * Use contextual stores sparingly; excessive reliance can lead to performance bottlenecks. * Implement custom equality checks when using mutable data structures inside stores. **Don't Do This:** * Avoid mutating objects or arrays directly inside stores when using "$:" as it triggers updates regardless of changes. Use ".set()" or ".update()" on the store. * Don't update stores with the same value. This is especially true with writable stores. **Example:** """svelte <script> import { derived, writable } from 'svelte/store'; // Writable store for user input const inputText = writable(''); // Derived store for debounced input value - Example const debouncedText = derived(inputText, ($inputText, set) => { const timeout = setTimeout(() => { set($inputText); }, 300); // Debounce time in milliseconds return () => clearTimeout(timeout); // Cleanup function }); // Local variable for internal state let counter = 0; function increment() { counter += 1; // Does NOT trigger rerender of the page. } // Updating the store correctly function updateInput(event) { inputText.set(event.target.value); } </script> <input type="text" on:input={updateInput} /> <p>Debounced Text: {$debouncedText}</p> <p>Counter: {counter}</p> <button on:click={increment}>Increment</button> """ ### 1.2. Use "{#each}" Blocks Efficiently **Standard:** Optimize "{#each}" blocks for performance by providing unique keys and avoiding unnecessary re-renders of list items. **Why:** Svelte's "{#each}" block is efficient, but incorrect usage can lead to performance bottlenecks if items are frequently added, removed, or reordered. **Do This:** * Provide a unique "key" attribute to each item in the list. This helps Svelte identify which items have changed and only update those specific elements. * Use immutable data structures when rendering lists from stores. If data changes, create new copies. If the data isn't immutable, make sure to use the key binding, or Svelte will not know what has changed and re-render everything. * Consider using pagination or virtualization for very long lists to avoid rendering all items at once. **Don't Do This:** * Avoid using the index as a key if the list items can be reordered, as this can cause incorrect updates. This is a frequent source of bugs. * Don't mutate list data directly; instead, create new arrays or objects to trigger updates. **Example:** """svelte <script> import { writable } from 'svelte/store'; const items = writable([ { id: 1, name: 'Item A' }, { id: 2, name: 'Item B' }, { id: 3, name: 'Item C' } ]); function addItem() { items.update(currentItems => [...currentItems, { id: Date.now(), name: 'New Item' }]); } </script> <button on:click={addItem}>Add Item</button> {#each $items as item (item.id)} <!-- Explicit Key Binding --> <div key={item.id}> {item.name} </div> {/each} """ ### 1.3. Optimize Component Size **Standard:** Keep components focused and relatively small to reduce rendering overhead. **Why:** Large, complex components can be challenging to reason about and can lead to slower rendering times. **Do This:** * Break down large components into smaller, reusable components. * Move complex logic into separate functions or modules. * Use slots for flexible content injection. **Don't Do This:** * Don't create "God Components" that handle too much logic and rendering. **Example:** Instead of: """svelte <!-- Bad example of a large component --> <script> let data = [...]; //Lots of data let filter = ''; let sortOrder = 'asc'; function filterData() { ... } function sortData() { ... } function renderItem(item) { ... } </script> <div> <input bind:value={filter}/> <button on:click={sortData}>Sort</button> {#each filterData(data) as item} {renderItem(item)} {/each} </div> """ Do This: Split it into smaller components: """svelte <!-- Good Example --> <script> import FilterInput from './FilterInput.svelte'; import SortButton from './SortButton.svelte'; import ItemList from './ItemList.svelte'; let data = [...]; let filter = ''; let sortOrder = 'asc'; function sortData() { ... } </script> <div> <FilterInput bind:filter={filter} /> <SortButton on:click={sortData} /> <ItemList data={data} filter={filter} /> </div> """ ## 2. Code Splitting and Lazy Loading ### 2.1. Implement Code Splitting **Standard:** Split your application into smaller chunks to reduce the initial load time. **Why:** Code splitting allows browsers to download only critical code initially, improving the perceived performance of your application. Svelte integrates with bundlers like Vite and Rollup, making code splitting relatively straightforward. **Do This:** * Use dynamic imports ("import()") to load components or modules on demand. * Configure your bundler (Vite/Rollup) to automatically split code based on import statements. * Lazy-load routes in your application. **Don't Do This:** * Avoid loading all your application code in a single bundle. **Example:** """svelte <script> let component; async function loadComponent() { component = (await import('./MyComponent.svelte')).default; } </script> {#if component} <svelte:component this={component} /> {:else} <button on:click={loadComponent}>Load Component</button> {/if} """ ### 2.2. Lazy Load Images and Other Assets **Standard:** Defer the loading of non-critical assets like images until they are needed. **Why:** Lazy loading reduces initial page load time and conserves bandwidth by only loading assets when they are visible in the viewport. **Do This:** * Use the "loading="lazy"" attribute for images. * Implement custom lazy loading for components or sections of your application. * Consider using a library like "svelte-lazy" for more advanced lazy-loading scenarios. **Don't Do This:** * Avoid loading all images on page load, especially for long pages. **Example:** """svelte <img src="my-image.jpg" alt="My Image" loading="lazy"> """ ## 3. Binding and Event Handling ### 3.1. Optimize Event Listeners **Standard:** Use passive event listeners for scroll and touch events to improve scrolling performance. Also, use delegated events when appropriate. **Why:** Passive event listeners allow the browser to handle scrolling smoothly without waiting for JavaScript to execute. **Do This:** * Add the "passive: true" option to event listeners for scroll and touch events. * Use delegated events where multiple child element needs to listen to the same event. **Don't Do This:** * Avoid blocking the main thread with long-running event handlers during scrolling. **Example:** """svelte <svelte:window on:scroll={() => { // Your scroll handling logic here }} options={{ passive: true }} /> """ ### 3.2. Minimize Two-Way Binding **Standard:** Use two-way binding ("bind:value") only when necessary, as it can lead to performance issues with frequent updates. **Why:** Two-way binding can trigger frequent component updates, especially with text inputs. **Do This:** * Use one-way binding and event listeners for more control over updates. * Consider debouncing input events to reduce the frequency of updates. **Don't Do This:** * Avoid using two-way binding for large data sets or complex components. **Example:** Instead of: """svelte <!-- Potentially problematic two-way binding --> <input bind:value={myValue} /> """ Do This: """svelte <script> import { writable } from 'svelte/store'; const myValue = writable(""); function updateValue(event) { myValue.set(event.target.value); } </script> <input type="text" on:input={updateValue} /> <p>Value: {$myValue}</p> """ ## 4. Svelte Specific Optimizations ### 4.1. Leveraging Svelte's Reactivity **Standard:** Utilize Svelte's built-in reactivity features effectively to minimize manual DOM manipulation. **Why:** Svelte's compiler optimizes updates efficiently. Avoid directly manipulating the DOM unless absolutely necessary. **Do This:** * Use reactive declarations ("$:") for derived values and side effects. * Rely on Svelte's component updates for rendering changes. * Use transition directives instead of manually handling animations whenever possible. **Don't Do This:** * Avoid using "document.querySelector" or similar methods to directly manipulate the DOM within Svelte components unless critically necessary. **Example:** """svelte <script> let count = 0; $: doubled = count * 2; // Reactive declaration function increment() { count += 1; } </script> <button on:click={increment}>Increment</button> <p>Count: {count}</p> <p>Doubled: {doubled}</p> """ ### 4.2. Efficient Use of Context API **Standard:** Use the Svelte context API thoughtfully, balancing convenience with potential performance implications. **Why:** Excessive use of context, especially with frequent updates, can cause unnecessary re-renders in components that depend on the context. **Do This:** * Provide context at the highest level possible to minimize the number of components affected by context changes. * Consider using stores instead of context for data that needs to be shared and updated frequently. * Memoize values passed via context if they are expensive to compute. **Don't Do This:** * Avoid updating context values unnecessarily. **Example:** """svelte <script> import { setContext } from 'svelte'; import { writable } from 'svelte/store'; // Create a store to hold context data const myContext = writable({ value: 'Initial Value' }); // Set the context value setContext('myContextKey', myContext); </script> <slot /> """ """svelte <script> import { getContext } from 'svelte'; import { onMount } from 'svelte'; // Get the context value const myContext = getContext('myContextKey'); let value; // Subscribe to the store to update the local variable const unsubscribe = myContext.subscribe(val => { value = val.value; }); onMount(() => { return () => unsubscribe(); }); //Unsubscribing on destory is imporant. </script> <p>Context Value: {value}</p> """ ## 5. External Libraries and Tooling ### 5.1. Use SvelteKit's Load Function Wisely **Standard**: Return only the data you need and avoid unnecessary computations in load functions. **Why**: Load functions run on both the server and the client. Expensive operations can slow down both the initial server render and subsequent client-side navigations. **Do This**: * Fetch only the data necessary for the current page. Avoid fetching entire databases without pagination or filtering. * Move complex data transformations or business logic into separate modules and cache the results if appropriate. * Utilize the "depends" function to invalidate the "load" function only when necessary dependencies change. **Don't Do This**: * Perform database queries directly within components. This breaks separation of concerns and couples your components to the backend. * Include large, unused datasets in the returned "load" function data. **Example:** """typescript // src/routes/blog/[slug]/+page.ts import { getBlogPost } from '$lib/blog'; // Assume this fetches a blog post from a database or CMS export async function load({ params, fetch }) { const post = await getBlogPost(params.slug, fetch); //Assuming getBlogPost now uses fetch api if (!post) { error(404, 'Post not found'); } return { title: post.title, content: post.content, // Only return the necessary parts of the post slug: params.slug }; } """ ### 5.2 Performance Monitoring **Standard**: Use browser developer tools to identify and address performance bottlenecks. **Why**: Profilers and performance monitoring tools are the most reliable way to identify and resolve perfomance related issues **Do This**: * Use the "Performance" tab in Chrome DevTools (or similar in other browsers) to record and analyze the performance of your Svelte applications. * Identify slow-rendering components, long-running JavaScript functions, and excessive DOM manipulations. * Use Svelte Devtools for Component inspection * Use Lighthouse reporting to identify key areas for performance improvement. **Don't Do This**: * Rely solely on intuition. Performance is often counterintuitive. ## 6. Security Considerations While this document is primarily focused on performance, security and performance often intersect: ### 6.1. Sanitize User Input **Standard:** Always sanitize user input to prevent XSS (Cross-Site Scripting) vulnerabilities, which can negatively impact performance by injecting malicious code. **Why:** XSS vulnerabilities allow attackers to inject malicious scripts into your application, potentially leading to data theft, session hijacking, or defacement of your website. Additionally, unsanitized inputs may contain special characters or escape sequences that can lead to unpredictable behavior. **Do This:** * Use Svelte's built-in HTML encoding to prevent malicious code from being injected into your application. * Employ a library like DOMPurify to sanitize HTML input from users. * Avoid using "{@html ...}" directive with user-provided data without proper sanitization. **Don't Do This:** * Never trust user input directly without proper sanitization. **Example:** """svelte <script> import DOMPurify from 'dompurify'; let userInput = ''; let sanitizedHTML = ''; function sanitizeInput() { sanitizedHTML = DOMPurify.sanitize(userInput); } </script> <input bind:value={userInput} on:input={sanitizeInput} /> <div> {@html sanitizedHTML} </div> """ ### 6.2. Secure API Communication **Standard:** Use HTTPS for all API communication to protect data in transit and prevent man-in-the-middle attacks. **Why:** Using HTTPS ensures that data exchanged between your application and backend API is encrypted, preventing eavesdropping and tampering. Unencrypted communication can expose sensitive information and compromise user data. **Do This:** * Ensure all API endpoints use HTTPS (SSL/TLS) encryption. * Use appropriate CORS (Cross-Origin Resource Sharing) policies to restrict access to your API. * Validate and sanitize data received from the API to prevent injection attacks. **Don't Do This:** * Avoid using HTTP for sensitive data transmission or API communication in production environments. ### 6.3. Proper Error Handling **Standard:** Implement robust error handling to prevent information leakage and denial-of-service attacks. **Why:** Proper error handling prevents sensitive information from being exposed in error messages and prevents attackers from exploiting vulnerabilities to crash the application. **Do This:** * Handle exceptions and errors gracefully and log relevant information for debugging. * Avoid revealing sensitive information (e.g., database connection strings, API keys) in error messages. * Implement rate limiting to prevent abuse and denial-of-service attacks. **Don't Do This:** * Avoid displaying detailed error messages directly to users in production environments. This document provides a starting point for defining Svelte coding standards focused on performance optimization. It should evolve as the framework and best practices change and should be tailored to the specific needs of each project.
# Code Style and Conventions Standards for Svelte This document outlines the code style and conventions standards for Svelte projects. Adhering to these guidelines promotes code readability, maintainability, and consistency across the project. These standards aim to provide best practices tailored to the latest version of Svelte. Enforcing these standards assists AI coding assistants (like GitHub Copilot, Cursor) in generating code that is compliant and coherent. ## 1. Formatting and Whitespace Consistent formatting is crucial for code readability. ### 1.1. Indentation **Do This:** * Use 2 spaces for indentation. Avoid tabs. * Configure your editor to automatically convert tabs to spaces. **Don't Do This:** * Mix tabs and spaces. * Use a different number of spaces for indentation. **Why:** Consistent indentation makes the code structure obvious and reduces visual clutter. **Example:** """svelte <!-- Correct --> <div> {#if condition} <p>This is a paragraph.</p> {/if} </div> <!-- Incorrect --> <div> {#if condition} <p>This is a paragraph.</p> {/if} </div> """ ### 1.2. Line Length **Do This:** * Limit lines to a maximum of 120 characters. * Break long lines to improve readability. **Don't Do This:** * Have extremely long lines that require horizontal scrolling. **Why:** Limiting line length improves readability, especially on smaller screens. **Example:** """svelte <!-- Correct --> <script> import { someVeryLongFunctionName } from './utils'; let someVeryLongVariableName = someVeryLongFunctionName( 'argument1', 'argument2', 'argument3' ); </script> <!-- Incorrect --> <script>import { someVeryLongFunctionName } from './utils'; let someVeryLongVariableName = someVeryLongFunctionName('argument1', 'argument2', 'argument3');</script> """ ### 1.3. Whitespace **Do This:** * Use a single blank line between top-level constructs (e.g., between "<script>", "<style>", and HTML). * Use spaces around operators and after commas. * Ensure consistent spacing within HTML attributes. * Use a newline at the end of each file. **Don't Do This:** * Omit whitespace around operators. * Use multiple blank lines unnecessarily. * Forget the trailing newline. **Why:** Proper whitespace enhances code clarity significantly. **Example:** """svelte <!-- Correct --> <script> let count = 0; function increment() { count += 1; } </script> <style> button { padding: 10px; } </style> <div> <button on:click={increment}>Click me</button> <p>Count: {count}</p> </div> <!-- Incorrect --> <script> let count=0; function increment(){ count+=1; } </script> <style>button{padding:10px;}</style> <div><button on:click={increment}>Click me</button><p>Count: {count}</p></div> """ ## 2. Naming Conventions Consistent naming conventions are critical for code understanding and maintainability. ### 2.1. Variables and Constants **Do This:** * Use camelCase for variable and constant names (e.g., "myVariable", "userCount"). * Use UPPER_SNAKE_CASE for constants (e.g., "API_URL", "DEFAULT_TIMEOUT"). * Use descriptive and meaningful names. **Don't Do This:** * Use single-letter variable names (except in "for" loops). * Use abbreviations that are not widely understood. **Why:** Clearly named variables and constants make the code self-documenting. **Example:** """svelte <script> // Correct let userAge = 30; const API_ENDPOINT = '/api/users'; // Incorrect let a = 30; const url = '/api/users'; </script> """ ### 2.2. Components **Do This:** * Use PascalCase for component names (e.g., "MyComponent", "UserProfile"). * Component files should match the component name (e.g., "MyComponent.svelte"). * Create reusable components for repeating patterns. **Don't Do This:** * Use lowercase or snake_case for component names. * Include logic in root App.svelte file if possible. * Use cryptic component names. **Why:** Consistent component naming helps distinguish components from other elements. **Example:** """ src/ ├── components/ │ ├── UserProfile.svelte │ └── ProductCard.svelte └── App.svelte """ Within "UserProfile.svelte": """svelte <!-- Correct --> <script> export let user; </script> <div> <h1>{user.name}</h1> <p>Email: {user.email}</p> </div> <!-- Incorrect --> <script> export let data; </script> <div> <h1>{data.name}</h1> <p>Email: {data.email}</p> </div> """ ### 2.3. Functions **Do This:** * Use camelCase for function names (e.g., "getData", "calculateTotal"). * Use descriptive names that indicate the function's purpose. **Don't Do This:** * Use vague or ambiguous function names. * Use abbreviations unless they are universally understood. **Why:** Clear function names improve code readability and maintainability. **Example:** """svelte <script> // Correct function calculateTotal(items) { return items.reduce((sum, item) => sum + item.price, 0); } // Incorrect function calc(items) { return items.reduce((sum, item) => sum + item.price, 0); } </script> """ ### 2.4. Props **Do This:** * Use camelCase: For props passed to Svelte components. * Use Typescript typing: For explicit type declarations. **Don't Do This:** * Not documenting props or using PropTypes (since Svelte uses Typescript for props). * Using snake_case instead of camelCase. **Why:** Explicit props improve code readability and maintainability. With the introduction of Svelte 5 and newer conventions, it is essential to align with the latest standards. **Example:** """svelte <!-- Correct --> <script lang="ts"> export let userName: string; export let isLoggedIn: boolean = false; </script> <h1>Hello, {userName}!</h1> {#if isLoggedIn} <p>Welcome back!</p> {/if} <!-- Incorrect --> <script> export let user_name; export let is_logged_in = false; </script> <h1>Hello, {user_name}!</h1> {#if is_logged_in} <p>Welcome back!</p> {/if} """ ## 3. Svelte-Specific Style ### 3.1. Script Tags **Do This:** * Place "<script>" tags either at the top or bottom of the component file. * Use "<script lang="ts">" for TypeScript support. **Don't Do This:** * Mix "<script>" tags throughout the HTML unless absolutely necessary. **Why:** Clear separation of logic and markup improves readability. **Example:** """svelte <!-- Correct --> <script lang="ts"> export let message: string; </script> <h1>{message}</h1> <!-- Incorrect --> <h1>{message}</h1> <script> export let message; </script> """ ### 3.2. Style Tags **Do This:** * Use "<style>" tags to encapsulate component-specific styles. * Use ":global(...)" selector sparingly, only when necessary to override global styles. **Don't Do This:** * Define global styles directly in component style tags. * Use inline styles unless for very specific, dynamic styling. **Why:** Scoped styles prevent style conflicts and improve maintainability. **Example:** """svelte <!-- Correct --> <style> h1 { color: blue; } </style> <!-- Incorrect --> <style> :global(body) { font-family: Arial; } </style> """ ### 3.3. Reactive Declarations **Do This:** * Use reactive declarations ("$:") for automatically updating values based on dependencies. * Avoid complex logic inside reactive declarations; delegate to functions. **Don't Do This:** * Overuse reactive declarations for non-reactive logic. * Mutate reactive variables directly without reassignment. **Why:** Reactive declarations simplify state management and ensure data consistency. **Example:** """svelte <script> let price = 10; let quantity = 2; $: total = price * quantity; // Correct function updateTotal() { price = 20; // Correct, triggers reactivity } </script> <p>Total: {total}</p> <button on:click={updateTotal}>Update Price</button> """ ### 3.4. Event Handling **Do This:** * Use "on:" directives for event handling (e.g., "on:click", "on:submit"). * Keep event handlers concise and delegate complex logic to functions. **Don't Do This:** * Write lengthy event handlers directly in the template. **Why:** Separating event handling logic improves readability and testability. **Example:** """svelte <script> function handleClick() { alert('Button clicked!'); } </script> <button on:click={handleClick}>Click me</button> """ ### 3.5. Component Directives and Attributes **Do This:** * Use directives like "bind:", "transition:", "animate:", and "use:" for component enhancements. * Use descriptive attribute names for clarity. * Consider short-hand syntax where appropriate, e.g. prop **Don't Do This:** * Overuse or misuse directives leading to unnecessary complexity. * Use obscure attribute names. **Why:** Directives offer powerful ways to integrate behaviors and effects declaratively. **Example:** """svelte <script> import { fade } from 'svelte/transition'; let visible = false; let inputValue = ''; </script> <input bind:value={inputValue} /> <button on:click={() => visible = !visible}>Toggle</button> {#if visible} <p transition:fade>This will fade in and out.</p> {/if} """ ### 3.6. Stores **Do This:** * Use Svelte stores for managing global state. * Use the "$" prefix to access store values in the template. * Create custom stores for complex state management logic. **Don't Do This:** * Overuse stores for component-local state. * Mutate store values directly without using "set" or "update". **Why:** Stores provide a centralized and reactive way to manage application state. **Example:** """svelte <script> import { count } from './stores'; function increment() { $count += 1; } </script> <button on:click={increment}>Increment</button> <p>Count: {$count}</p> """ ### 3.7. Each Blocks **Do This:** * Include a "key" attribute when using "{#each}" blocks to help Svelte efficiently update the DOM. The key should be unique and stable. * Keep the logic inside "{#each}" blocks simple. If complex, delegate to a component. **Don't Do This:** * Omit the "key" attribute when rendering dynamic lists. * Perform complex operations directly within the "{#each}" block. **Why:** Keys enable Svelte to efficiently update the DOM when list items change. **Example:** """svelte <script> let items = [ { id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 3, name: 'Orange' } ]; </script> {#each items as item (item.id)} <p>{item.name}</p> {/each} """ ## 4. Modern Approaches and Patterns (Svelte 5) With the advent of Svelte 5, adopting recent conventions is crucial. ### 4.1. Reactive Statements in Svelte 5 **Do This:** * Use explicit declarations for reactivity like "$derived" and "$effect". * Understand the nuances between "$derived", which creates a new value reactively, and "$effect", which runs side effects. **Don't Do This:** * Rely on automatic reactivity for all variable assignments (which is deprecated). **Why:** Explicit reactivity enhances clarity and predictability in your code. **Example:** """svelte <script> let a = 1; let b = 2; $: sum = a + b; // Shorthand syntax for $derived $: { // Shorthand syntax for $effect console.log('The sum is', sum); } function updateValues() { a += 1; b += 1; } </script> <p>a: {a}</p> <p>b: {b}</p> <p>Sum: {sum}</p> <button on:click={updateValues}>Update Values</button> """ ### 4.2. Enhanced State Management **Do This:** * Take advantage of new store features and patterns available in the latest Svelte versions. * Adopt readable store patterns for complex application states. **Don't Do This:** * Ignore the advancements; keep using outdated approaches unnecessarily. **Why:** Modern state management improves code organization and maintainability. **Example:** """svelte <script> import { writable } from 'svelte/store'; const count = writable(0); function increment() { count.update(n => n + 1); } </script> <button on:click={increment}>Increment</button> <p>Count: {$count}</p> """ ### 4.3. Optimizing Performance **Do This:** * Use "{#each}" blocks with proper "key" attributes. * Leverage "svelte:window", "svelte:document", and "svelte:body" for optimized event handling. **Don't Do This:** * Attach global event listeners directly without using these optimized elements. **Why:** Optimized event handling helps in improving application performance. **Example:** """svelte <svelte:window on:keydown={handleKeyDown} /> <script> function handleKeyDown(event) { console.log('Key pressed:', event.key); } </script> """ ### 4.4 Component Composition and Reusability **Do This:** * Favor composition over inheritance. Build complex UIs by combining smaller, reusable components. * Use slots effectively to create flexible components that accept custom content. * Write clear and concise prop definitions with TypeScript to ensure type safety and prevent runtime errors. **Don't Do This:** * Create monolithic components with excessive logic and markup. * Rely on deep component hierarchies that are difficult to understand and maintain. * Neglect to document component props, making it harder for others to use them correctly. **Why:** Encourages reusability and separation of concerns, making applications easier to maintain and scale. **Example:** """svelte // Reusable Button Component <script lang="ts"> export let label: string; export let onClick: () => void; </script> <button on:click={onClick}>{label}</button> // Usage in Parent Component <script> import Button from './Button.svelte'; function handleClick() { alert('Button Clicked!'); } </script> <Button label="Click Me" onClick={handleClick} /> """ ### 4.5 Security Considerations **Do This:** * Sanitize user inputs to prevent cross-site scripting (XSS) attacks. * Use Content Security Policy (CSP) to mitigate the risk of injecting malicious code into your application. * Validate data on the server-side in addition to client-side validation. **Don't Do This:** * Directly render unsanitized user inputs in your templates. * Rely solely on client-side validation for security. * Store sensitive information in the browser's local storage or cookies without proper encryption. **Why:** Security vulnerabilities can compromise your application and expose user data. **Example:** """svelte <script> import { onDestroy } from 'svelte'; let inputValue = ''; // Function to sanitize input value function sanitizeInput(value) { // Implement your sanitization logic here return value.replace(/</g, '<').replace(/>/g, '>'); } // Reactive statement to update sanitized value $: sanitizedValue = sanitizeInput(inputValue); </script> <input type="text" bind:value={inputValue} /> <!-- Display the sanitized value --> <p>Sanitized Input: {@html sanitizedValue}</p> """ ## 5. Tooling and Automation ### 5.1. Linters and Formatters **Do This:** * Use ESLint with Svelte-specific plugins for linting. * Use Prettier for automatic code formatting. * Integrate these tools into your development workflow (e.g., using Git hooks). **Don't Do This:** * Ignore linting and formatting errors. * Manually format code when automated tools are available. **Why:** Linters and formatters enforce code style and help catch potential errors. ### 5.2. Testing **Do This:** * Write unit tests for components using testing frameworks like Jest or Vitest. * Use component testing libraries to ensure component behavior. * Write end-to-end tests for critical user flows. **Don't Do This:** * Skip writing tests. * Rely solely on manual testing. **Why:** Automated tests ensure code quality and prevent regressions. ### 5.3. Documentation **Do This:** * Use JSDoc or TypeScript annotations to document components, functions, and props. * Maintain a README file with clear instructions on how to set up and run the project. * Use tools like Storybook to document and showcase components. **Don't Do This:** * Neglect writing documentation. * Write inaccurate or outdated documentation. **Why:** Documentation makes it easier for others (and your future self) to understand and maintain the code. ## 6. Conclusion Adhering to these code style and conventions will significantly enhance the quality, readability, and maintainability of Svelte projects. Consistently following these guidelines ensures that the codebase remains clean, understandable, and easy to collaborate on. Furthermore, enforcing these standards allows AI coding assitants to provide compliant and high-quality code suggestions. By adopting these best practices, development teams can build robust and scalable Svelte applications.
# API Integration Standards for Svelte This document outlines the recommended coding standards for integrating APIs within Svelte applications. Following these standards will promote maintainability, performance, security, and overall code quality. It assumes familiarity with Svelte's core concepts and common web development practices. ## 1. Architectural Patterns for API Integration Choosing the right architectural pattern is crucial for a scalable and maintainable Svelte application. This section covers common patterns for handling API requests. ### 1.1. Services Pattern The Services Pattern encapsulates API interaction logic (data fetching, error handling, request formatting) within reusable service modules. **Standard:** Isolate API interaction logic into dedicated service modules. **Do This:** Create separate ".js" or ".ts" files for each API endpoint or group of related endpoints. **Don't Do This:** Embed API calls directly within Svelte components. **Why:** * **Improved Code Organization:** Keeps components focused on presentation and user interaction. * **Reusability:** Services can be reused across multiple components and even different applications. * **Testability:** Services are easier to test in isolation. * **Reduced Component Complexity:** Keeps components lean and readable. **Example:** """javascript // src/services/userService.js const API_BASE_URL = 'https://api.example.com'; async function getUser(id) { try { const response = await fetch("${API_BASE_URL}/users/${id}"); if (!response.ok) { throw new Error("HTTP error! status: ${response.status}"); } return await response.json(); } catch (error) { console.error('Error fetching user:', error); throw error; // Re-throw to be handled by the component } } async function createUser(userData) { try { const response = await fetch("${API_BASE_URL}/users", { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData), }); if (!response.ok) { throw new Error("HTTP error! status: ${response.status}"); } return await response.json(); } catch (error) { console.error("Error creating user:", error); throw error; } } export { getUser, createUser }; // Export multiple functions as an object """ """svelte <!-- src/components/UserProfile.svelte --> <script> import { onMount } from 'svelte'; import { getUser } from '../services/userService'; // Import the service let user = null; export let userId; onMount(async () => { try { user = await getUser(userId); } catch (error) { // Handle errors gracefully (e.g., display an error message) console.error('Failed to load user:', error); alert('Failed to load user.'); } }); </script> {#if user} <h1>{user.name}</h1> <p>Email: {user.email}</p> {:else} <p>Loading user...</p> {/if} <!-- Another component, e.g., UserForm.svelte --> <script> import { createUser } from '../services/userService'; async function handleSubmit(event) { event.preventDefault(); const formData = new FormData(event.target); const userData = Object.fromEntries(formData.entries()); try { const newUser = await createUser(userData); console.log('User created:', newUser); // Redirect or update the UI } catch (error) { console.error('Error creating user:', error); alert("Error creating user!"); } } </script> <form on:submit={handleSubmit}> <label for="name">Name:</label> <input type="text" id="name" name="name" required> <label for="email">Email:</label> <input type="email" id="email" name="email" required> <button type="submit">Create User</button> </form> """ ### 1.2. Store-Based Data Management Svelte stores provide a reactive data container, ideal for managing data fetched from APIs. **Standard:** Use Svelte stores to manage the application's state related to API data. **Do This:** Create writable, readable, or derived stores to hold and manipulate API responses. **Don't Do This:** Directly mutate component-level variables with API data, bypassing reactivity. **Why:** * **Reactivity:** Changes to the store automatically trigger updates in subscribed components. * **Centralized State:** Provides a single source of truth for application data. * **Simplified Data Sharing:** Easier to share data between components without prop drilling. * **Simplified Testing:** Stores are easier to test outside of components. * **Fine-grained updates:** Only components using parts of the store that changed will re-render. **Example:** """javascript // src/stores/userStore.js import { writable } from 'svelte/store'; import { getUser } from '../services/userService'; const userStore = writable({ data: null, loading: false, error: null, }); async function loadUser(id) { userStore.update(state => ({ ...state, loading: true, error: null })); try { const userData = await getUser(id); userStore.set({ data: userData, loading: false, error: null }); } catch (error) { userStore.set({ data: null, loading: false, error }); } } export { userStore, loadUser }; """ """svelte <!-- src/components/UserProfile.svelte --> <script> import { onMount } from 'svelte'; import { userStore, loadUser } from '../stores/userStore'; export let userId; onMount(() => { loadUser(userId); }); </script> {#if $userStore.loading} <p>Loading user...</p> {:else if $userStore.error} <p>Error: {$userStore.error.message}</p> {:else if $userStore.data} <h1>{$userStore.data.name}</h1> <p>Email: {$userStore.data.email}</p> {:else} <p>User not found</p> {/if} """ ### 1.3. Server-Side Rendering (SSR) and API Integration For performance and SEO benefits, consider Server-Side Rendering (SSR) with SvelteKit. **Standard:** Fetch initial data on the server for faster initial load times. **Do This:** Utilize SvelteKit's "load" function within "+page.server.js" for server-side API calls. (Note: filename might change to "+server.js" depending on SvelteKit version.) **Don't Do This:** Perform all API calls on the client, especially for critical content. **Why:** * **Improved SEO:** Search engines can easily crawl and index content rendered server-side. * **Faster Initial Load:** Users see content faster, improving user experience. * **Reduced Client-Side Processing:** Offloads work from the client's browser, especially on low-powered devices. **Example:** """javascript // src/routes/users/[userId]/+page.server.js import { getUser } from '../../../services/userService'; export async function load({ params }) { const { userId } = params; try { const user = await getUser(userId); return { user, }; } catch (error) { return { status: 500, error: 'Failed to load user', }; } } """ """svelte <!-- src/routes/users/[userId]/+page.svelte --> <script> export let data; // data passed from the load function $: ({ user } = data); </script> {#if user} <h1>{user.name}</h1> <p>Email: {user.email}</p> {:else} <p>Failed to load user.</p> {/if} """ ## 2. Data Fetching Best Practices Efficient data fetching is paramount for a responsive Svelte application. ### 2.1. Async/Await **Standard:** Use "async/await" syntax for cleaner and more readable asynchronous code. **Do This:** Wrap API calls in "async" functions and use "await" to handle promises. **Don't Do This:** Rely on ".then()" callbacks, which can lead to complex and hard-to-read code. **Why:** * **Improved Readability:** "async/await" makes asynchronous code look and behave more like synchronous code. * **Easier Error Handling:** Simplifies error handling with "try/catch" blocks. **Example:** """javascript async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; } catch (error) { console.error('Error fetching data:', error); throw error; // Re-throw for component-level handling } } """ ### 2.2. Error Handling **Standard:** Implement robust error handling to gracefully manage API failures. **Do This:** Wrap API calls in "try/catch" blocks and provide user-friendly error messages. Log errors to the console or a logging service. Consider using a dedicated error reporting service (e.g., Sentry). **Don't Do This:** Ignore errors or display generic and unhelpful error messages. **Why:** * **Improved User Experience:** Prevents the application from crashing or displaying cryptic errors. * **Easier Debugging:** Provides valuable information for diagnosing and resolving issues. **Example:** """javascript async function fetchData() { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) { throw new Error("HTTP error! status: ${response.status}"); } const data = await response.json(); return data; } catch (error) { console.error('Error fetching data:', error); alert('An error occurred while fetching data. Please try again later.'); // User-friendly message throw error; // Re-throw if component needs to handle it } } """ ### 2.3. Loading Indicators **Standard:** Provide visual feedback to users during API requests. **Do This:** Display loading indicators (e.g., spinners, progress bars) while data is being fetched. Hide the indicator once the data is loaded or an error occurs. **Don't Do This:** Leave the user in the dark without any indication of activity. **Why:** **Improved User Experience:** Keeps users informed and prevents them from thinking the application is unresponsive. **Example:** """svelte <script> import { onMount } from 'svelte'; let data = null; let loading = true; let error = null; onMount(async () => { try { const response = await fetch('https://api.example.com/data'); data = await response.json(); } catch (e) { error = e; console.error("Error during fetch:", e); } finally { loading = false; } }); </script> {#if loading} <p>Loading...</p> {:else if error} <p>Error: {error.message}</p> {:else if data} <pre>{JSON.stringify(data, null, 2)}</pre> {:else} <p>No data available.</p> {/if} """ ### 2.4. Data Transformations **Standard:** Transform API responses into a format suitable for the application's needs. **Do This:** Create functions to map, filter, or reshape data from the API before storing it in stores or displaying it in components. Ensure that you handle possible null/undefined values. **Don't Do This:** Directly use API responses without any processing, which can lead to inconsistencies and inflexibility. **Why:** * **Decoupling:** Isolates the application from changes in the API's data structure. * **Improved Performance:** Optimizes data for specific use cases, such as filtering large data sets. * **Enhanced Readability:** Transforms data into a more human-readable format. **Example:** """javascript function transformUserData(apiData) { if (!apiData) return null; // Handle null or undefined cases return { id: apiData.id, fullName: "${apiData.firstName} ${apiData.lastName}", emailAddress: apiData.email || 'N/A', // Handle missing values }; } // usage in service async function getUser(id) { try { const response = await fetch("${API_BASE_URL}/users/${id}"); if (!response.ok) { throw new Error("HTTP error! status: ${response.status}"); } const apiResponse = await response.json(); return transformUserData(apiResponse); //Applying transform } catch (error) { console.error('Error fetching user:', error); throw error; // Re-throw to be handled by the component } } """ ## 3. API Client Libraries Leverage existing libraries to simplify API interaction. ### 3.1. Fetch API **Standard:** Utilize the built-in "fetch" API for making HTTP requests. **Do This:** Use "fetch" for simple API calls. Implement custom headers, request methods, and error handling as needed. **Don't Do This:** Rely on older libraries like "XMLHttpRequest" unless absolutely necessary. **Why:** * **Native Integration:** "fetch" is a standard web API supported by all modern browsers and Node.js. * **Promise-Based:** Provides a clean and modern API for asynchronous operations. **Example:** """javascript async function fetchData() { const response = await fetch('https://api.example.com/data', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer <token>', }, }); if (!response.ok) { throw new Error("HTTP error! status: ${response.status}"); } const data = await response.json(); return data; } """ ### 3.2. Axios **Standard:** Consider using Axios for advanced API interaction and features not available in the native "fetch" API. **Do This:** Use Axios for features like request cancellation, automatic JSON transformation, and interceptors. **Don't Do This:** Use Axios for simple API calls that can be easily handled by "fetch". **Why:** * **Feature-Rich:** Provides a wide range of features for advanced API interaction. * **Interceptors:** Allows you to intercept and modify requests and responses globally. * **Request Cancellation:** Enables you to cancel pending requests, which is useful for improving performance and user experience. **Example:** """javascript import axios from 'axios'; async function fetchData() { try { const response = await axios.get('https://api.example.com/data', { headers: { Authorization: 'Bearer <token>', }, }); return response.data; } catch (error) { console.error('Error fetching data:', error); throw error; } } // Example of request Interceptor axios.interceptors.request.use( config => { // Do something before request is sent config.headers['X-Request-Id'] = generateRequestId(); return config; }, error => { // Do something with request error return Promise.reject(error); } ); // Example of response Interceptor axios.interceptors.response.use( response => { // Any status code that lie within the range of 2xx cause this function to trigger // Do something with response data return response; }, error => { // Any status codes that falls outside the range of 2xx cause this function to trigger // Do something with response error if (error.response && error.response.status === 401) { // Handle unauthorized error (e.g., redirect to login) window.location.href = '/login'; } return Promise.reject(error); } ); """ ## 4. Security Considerations Protecting sensitive data and preventing vulnerabilities is critical. ### 4.1. Environment Variables **Standard:** Store API keys and other sensitive information in environment variables. **Do This:** Use ".env" files (in development) and environment variables on the server to store sensitive data. In SvelteKit, use "$env/static/private" for private environment variables. Access public environment variables via "$env/static/public". **Don't Do This:** Hardcode API keys or commit them to the repository. **Why:** * **Security:** Prevents sensitive data from being exposed in the codebase. * **Configuration:** Allows you to easily configure the application for different environments. **Example:** """ // .env API_KEY=your_api_key API_URL=https://api.example.com """ """javascript // svelte.config.js import adapter from '@sveltejs/adapter-auto'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import { loadEnv } from 'vite'; /** @type {import('@sveltejs/kit').Config} */ const config = { preprocess: vitePreprocess(), kit: { adapter: adapter() } }; export default config; // access from server files import { env } from '$env/static/private'; const apiKey = env.API_KEY; //access from public files (browser) import { env } from '$env/static/public'; const publicApiUrl = env.PUBLIC_API_URL """ ### 4.2. Input Validation **Standard:** Validate all user inputs before sending them to the API. **Do This:** Implement client-side and server-side validation to prevent malicious data from being sent to the API. **Don't Do This:** Trust user input without validation, which can lead to vulnerabilities like SQL injection or cross-site scripting (XSS). **Why:** * **Security:** Prevents malicious data from compromising the application or the API. * **Data Integrity:** Ensures that the data being sent to the API is valid and consistent. ### 4.3. CORS **Standard:** Configure Cross-Origin Resource Sharing (CORS) properly on the server. **Do This:** Set appropriate CORS headers on the API server to allow requests from the application's origin. **Don't Do This:** Use a overly permissive CORS configuration, opening up the API to potential security risks. **Why:** * **Security:** Prevents unauthorized access to the API from other domains. * **Functionality:** Ensures that the application can make requests to the API from different origins. ## 5. Performance Optimization Optimize API integration for speed and efficiency. ### 5.1. Caching **Standard:** Implement caching to reduce the number of API requests. **Do This:** Use browser caching, server-side caching (e.g., Redis), or a service worker to cache API responses. Use "stale-while-revalidate" strategy for fast initial loads and background updates. **Don't Do This:** Cache data aggressively without considering data freshness or potential inconsistencies. **Why:** * **Improved Performance:** Reduces network latency and server load. * **Offline Support:** Allows the application to function even when the user is offline. ### 5.2. Pagination **Standard:** Implement pagination for APIs that return large datasets. **Do This:** Use pagination parameters (e.g., "page", "limit") to retrieve data in smaller chunks. Display pagination controls in the UI to allow users to navigate through the data. **Don't Do This:** Load entire datasets at once, which can lead to performance issues and slow load times. **Why:** * **Improved Performance:** Reduces the amount of data that needs to be transferred and processed. * **Better User Experience:** Allows users to browse large datasets more efficiently. ### 5.3. Debouncing and Throttling **Standard**: Use debouncing and throttling for API calls triggered by user input. **Do This**: Debounce API calls for search inputs, so the API is only called once the user stops typing. Throttle API calls for scroll events to prevent excessive calls. **Don't Do This**: Make API calls on every keystroke for search, or every pixel scrolled down which can overwhelm the API. **Why:** * **Reduced Network Traffic:** Limits calls to the API, reducing server load. * **Improved Performance:** Avoids unnecessary processing on the server. * **Better User Experience:** Prevents UI lag from excessive API calls. ### 5.4. GraphQL **Standard**: Consider adopting GraphQL for complex data fetching needs. **Do This**: Use GraphQL when you need to fetch data from multiple resources, request only the data needed, and avoid over-fetching. **Don't Do This**: Use GraphQL for simple APIs, as it adds complexity to your infrastructure. **Why:** * **Efficient Data Fetching**: Only fetch the required data. * **Avoid Over-Fetching**: Prevent transferring unnecessary data. * **Schema Definition**: Provides type safety and improves documentation. ## 6. Testing Ensuring API integration works as expected. ### 6.1. Unit Tests **Standard**: Write unit tests for service functions and data transformations. **Do This**: Mock API calls and verify data transformations. **Don't Do This**: Skip unit testing API integration logic. ### 6.2. Integration Tests **Standard**: Test integration between components and the backend. **Do This**: Use a testing framework to simulate user interactions. Verify API calls are made correctly. **Don't Do This**: Deploy without testing API integration with actual components. ### 6.3. End-to-End Tests **Standard**: Write end-to-end tests to ensure the entire application works correctly. **Do This**: Simulate user workflows, from login to API calls, to ensure a smooth user experience. **Don't Do This**: Assume the application works without testing the whole flow. ## 7. Specific Svelte Considerations ### 7.1. Context API **Standard**: Use context API for passing data down deeply nested components. **Do This**: Create a context to hold API-related functions and state. **Don't Do This**: Overuse context; prop drilling may be simpler for a small number of components. """svelte <script> import { setContext } from 'svelte'; import { yourApiService } from './services'; // Create API-related context setContext('api', { fetchData: yourApiService.getData, // Add more functions or data that child components might need }); </script> <slot /> <!-- Render child components --> """ ### 7.2 Lifecycle Methods **Standard**: Use "onMount" for data fetching in components. **Do This**: Initiate API calls within the "onMount" lifecycle method to ensure component is mounted. **Don't Do This**: Make API calls outside lifecycle methods, which can lead to unexpected behavior. ### 7.3 Reactive Statements **Standard**: Use reactive statements for data manipulation and updates. **Do This**: Update the component when API responses come in. Use dependency arrays to react to specific store updates. **Don't Do This**: Directly manipulate DOM elements or skip reactivity, leading to inconsistent UI updates. ## 8. Documentation **Standard**: Document All API integrations clearly. **Do This:** Use JSDoc or Typescript annotations to detail request/response structures. Document error handling strategies. Add clear comments explaining the purpose of integration code. **Don't Do This:** Omit documentation leaving API integrations opaque and hard to maintain. ## CONCLUSION Adhering to these coding standards will result in more maintainable, performant, secure, and testable Svelte applications. By following these guidelines, development teams can establish consistency and reduce the likelihood of errors. Remember to adapt these guidelines as Svelte evolves, and as specific project needs arise. Regular code reviews are highly recommended to enforce these standards and ensure continuous improvement.