O SvelteKit utiliza um sistema de roteamento baseado em arquivos, onde a estrutura de diretórios do seu projeto define as URLs da aplicação. Esta abordagem simplifica a organização e o entendimento de como as rotas são mapeadas.
Estrutura Básica de Rotas
- O diretório
src/routesserve como a raiz da sua aplicação. - Criar um diretório como
src/routes/sobredefine a rota/sobre. - Para rotas dinâmicas, utilize colchetes:
src/routes/blog/[slug]cria uma rota onde[slug]é um parâmetro que pode ser acessado, como em/blog/meu-primeiro-post.
É possível modificar o diretório raiz das rotas através das configurações do projeto.
Cada diretório de rota contém um ou mais arquivos de rota, identificados pelo prefixo +. Algumas regras importantes para esses arquivos incluem:
- Todos os arquivos podem ser executados no servidor.
- Todos os arquivos, exceto
+server, também são executados no cliente. - Arquivos
+layoute+erroraplicam-se ao diretório onde estão e a todos os seus subdiretórios.
Arquivos de Página: +page
+page.svelte
Um componente +page.svelte define uma página específica na sua aplicação. Por padrão, a renderização inicial ocorre no servidor (SSR), e as navegações subsequentes são tratadas no navegador (CSR).
<!--- file: src/routes/+page.svelte --->
<h1>Bem-vindo ao Meu Site!</h1>
<a href="/about">Conheça a Página Sobre</a>
<!--- file: src/routes/about/+page.svelte --->
<h1>Sobre Nós</h1>
<p>Esta é a página "Sobre". Mais conteúdo em breve...</p>
<a href="/">Voltar à Página Inicial</a>
As páginas podem receber dados de uma função load através da propriedade data.
<!--- file: src/routes/articles/[id]/+page.svelte --->
<script>
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<h1>{data.articleTitle}</h1>
<div>{@html data.articleContent}</div>
O SvelteKit utiliza elementos <a> padrão para navegação entre rotas, sem a necessidade de componentes de link específicos do framwork.
+page.js
Para carregar dados que uma página necessita antes da renderização, adicione um módulo +page.js que exporta uma função load:
/// file: src/routes/articles/[id]/+page.js
import { error } from '@sveltejs/kit';
/** @type {import('./$types').PageLoad} */
export function load({ params }) {
const articles = {
'primeiro-artigo': {
articleTitle: 'Primeiro Artigo no Blog',
articleContent: '<p>Este é o conteúdo do seu primeiro artigo. Explore mais!</p>'
},
'segundo-artigo': {
articleTitle: 'Aprofundando no SvelteKit',
articleContent: '<p>Um olhar mais detalhado sobre os recursos do SvelteKit.</p>'
}
};
const article = articles[params.id];
if (article) {
return article;
}
error(404, 'Artigo não encontrado');
}
Esta função load executa-se tanto no servidor (durante o SSR) quanto no navegador (durante a navegação do cliente). Para detalhes completos da API, consulte a documentação da função load.
Além de load, +page.js pode exportar valores para configurar o comportamento da página:
export const prerender = true(oufalse,'auto')export const ssr = true(oufalse)export const csr = true(oufalse)
+page.server.js
Quando a sua função load precisa ser executada exclusivamente no servidor (por exemplo, para acesso a bancos de dados ou variáveis de ambiente privadas), você pode renomear +page.js para +page.server.js e alterar o tipo de PageLoad para PageServerLoad.
/// file: src/routes/products/[productId]/+page.server.js
// Simula uma função de acesso a banco de dados
const fetchProductFromDB = async (id) => {
// Em um cenário real, aqui haveria uma chamada a um DB
const products = {
'pdt-001': { productName: 'Laptop Ultrabook', price: 1200 },
'pdt-002': { productName: 'Teclado Mecânico', price: 150 }
};
return new Promise(resolve => setTimeout(() => resolve(products[id]), 100));
};
import { error } from '@sveltejs/kit';
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
const productInfo = await fetchProductFromDB(params.productId);
if (productInfo) {
return productInfo;
}
error(404, 'Produto não encontrado');
}
Durante a navegação no cliente, o SvelteKit carregará esses dados do servidor, o que significa que o valor retornado deve ser serializável com devalue.
Assim como +page.js, +page.server.js pode exportar opções de página como prerender, ssr e csr. Além disso, arquivos +page.server.js podem exportar *actions*, que permitem a interação com o servidor através de elementos <form> para gravação de dados.
Tratamento de Erros: +error
Em caso de erro durante a execução de uma função load, o SvelteKit exibe uma página de erro padrão. Você pode personalizar essa página adicionando um arquivo +error.svelte no diretório da rota.
<!--- file: src/routes/blog/[slug]/+error.svelte --->
<script>
import { page } from '$app/state';
</script>
<h1>Erro {page.status}: {page.error.message}</h1>
<p>Ocorreu um problema ao carregar esta página.</p>
O SvelteKit procura o limite de erro mais próximo "subindo" na árvore de diretórios. Se o arquivo acima não existir, ele tentará src/routes/blog/+error.svelte, depois src/routes/+error.svelte, antes de recorrer à página de erro padrão. Se todas as tentativas falharem (ou se o erro for lançado de uma função load de um +layout raiz), o SvelteKit renderizará uma página de erro estática de fallback, que pode ser personalizada através do arquivo src/error.html.
Um erro lançado dentro de uma função load de um +layout(.server).js será tratado pelo arquivo +error.svelte acima daquele layout na hierarquia, e não ao lado dele.
Erros 404 (rota não encontrada) são tratados por src/routes/+error.svelte ou pela página de erro padrão.
Note que erros que ocorrem no handle ou em manipuladores de requisição +server.js não utilizam +error.svelte.
Layouts Compartilhados: +layout
Para elementos que devem ser persistentes em várias páginas (como um cabeçalho de navegação ou rodapé), layouts são a solução ideal. Em vez de duplicar esses elementos em cada +page.svelte, eles são definidos em um arquivo de layout.
+layout.svelte
Crie src/routes/+layout.svelte para um layout que se aplica a todas as páginas. Um layout básico exige que o componente contenha a tag {@render children()} para onde o conteúdo da página será injetado.
<!--- file: src/routes/+layout.svelte --->
<script>
let { children } = $props();
</script>
<nav>
<a href="/">Início</a>
<a href="/servicos">Serviços</a>
<a href="/contato">Contato</a>
</nav>
<main>
{@render children()}
</main>
<footer>
<p>© 2024 Meu Aplicativo</p>
</footer>
Com este layout, navegar entre /, /servicos e /contato apenas substituirá o conteúdo dentro do <main>, mantendo o <nav> e <footer>.
Layouts podem ser aninhados. Por exemplo, para um conjunto de páginas como /configuracoes/perfil e /configuracoes/notificacoes que compartilham um sub-menu, você pode criar um layout específico para o diretório /configuracoes:
<!--- file: src/routes/configuracoes/+layout.svelte --->
<script>
/** @type {{ data: import('./$types').LayoutData, children: import('svelte').Snippet }} */
let { data, children } = $props();
</script>
<h1>Página de Configurações</h1>
<div class="submenu">
<h2>Navegação de Seções:</h2>
<ul>
{#each data.menuItems as item}
<li><a href="/configuracoes/{item.path}">{item.label}</a></li>
{/each}
</ul>
</div>
<div class="content-area">
{@render children()}
</div>
Por padrão, todo layout herda de seus layouts pai. Em cenários específicos onde isso não é desejado, layouts avançados oferecem mais controle.
+layout.js
Componentes +layout.svelte podem buscar dados através de uma função load definida em +layout.js, similar a como as páginas utilizam +page.js.
/// file: src/routes/configuracoes/+layout.js
/** @type {import('./$types').LayoutLoad} */
export function load() {
return {
menuItems: [
{ path: 'perfil', label: 'Meu Perfil' },
{ path: 'notificacoes', label: 'Notificações' },
{ path: 'privacidade', label: 'Privacidade' }
]
};
}
Se +layout.js exportar opções de página (prerender, ssr e csr), elas servirão como valores padrão para todas as páginas filhas. Os dados retornados pela função load de um layout também estão disponíveis para todas as suas páginas filhas.
+layout.server.js
Para executar a função load de um layout exclusivamente no servidor, mova-a para +layout.server.js e altere o tipo de LayoutLoad para LayoutServerLoad.
Similarmente a +layout.js, +layout.server.js pode exportar opções de página: prerender, ssr e csr.
Endpoints de Servidor: +server
Além das páginas, você pode definir rotas de API (ou "endpoints") usando arquivos +server.js, que concedem controle total sobre a resposta HTTP. Esses arquivos exportam funções correspondentes aos verbos HTTP (GET, POST, PATCH, PUT, DELETE, OPTIONS, HEAD). Cada função recebe um parâmetro RequestEvent e deve retornar um objeto Response.
Por exemplo, vamos criar uma rota /api/numero-aleatorio com um manipulador GET:
/// file: src/routes/api/numero-aleatorio/+server.js
import { error, json } from '@sveltejs/kit';
/** @type {import('./$types').RequestHandler} */
export function GET({ url }) {
const minVal = Number(url.searchParams.get('min') ?? '1');
const maxVal = Number(url.searchParams.get('max') ?? '100');
if (isNaN(minVal) || isNaN(maxVal) || minVal > maxVal) {
error(400, 'Os parâmetros min e max devem ser números válidos, e min não deve ser maior que max.');
}
const randomInt = Math.floor(Math.random() * (maxVal - minVal + 1)) + minVal;
return json({ value: randomInt, min: minVal, max: maxVal });
}
O primeiro argumento de Response pode ser um ReadableStream, o que é útil para transmitir grandes volumes de dados ou implementar Server-Sent Events (exceto em plataformas que bufferizam a resposta, como AWS Lambda).
Para sua conveniência, SvelteKit oferece funções auxiliares como error, redirect e json de @sveltejs/kit.
Quando um erro é lançado (seja por error(...) ou um erro inesperado), a resposta será um JSON de erro ou a página de erro de fallback (personalizável via src/error.html), dependendo do cabeçalho Accept. Componentes +error.svelte não são renderizados neste contexto.
Ao criar manipuladores OPTIONS, esteja ciente de que Vite injeta os cabeçalhos Access-Control-Allow-Origin e Access-Control-Allow-Methods durante o desenvolvimento; eles não aparecerão em produção a menos que você os adicione explicitamente.
Arquivos +layout não afetam arquivos +server.js. Para executar lógica antes de cada requisição do servidor, utilize o handle hook.
Recebendo Dados
Arquivos +server.js com manipuladores POST, PUT, PATCH, DELETE, OPTIONS ou HEAD podem ser usados para criar APIs RESTful.
<!--- file: src/routes/name-combiner/+page.svelte --->
<script>
let firstName = '';
let lastName = '';
let fullName = '';
async function combineNames() {
const response = await fetch('/api/combine-names', {
method: 'POST',
body: JSON.stringify({ name1: firstName, name2: lastName }),
headers: {
'content-type': 'application/json'
}
});
fullName = await response.json();
}
</script>
<input type="text" bind:value={firstName} placeholder="Primeiro Nome">
<input type="text" bind:value={lastName} placeholder="Sobrenome">
<p>Nome Completo: {fullName}</p>
<button onclick={combineNames}>Combinar</button>
/// file: src/routes/api/combine-names/+server.js
import { json } from '@sveltejs/kit';
/** @type {import('./$types').RequestHandler} */
export async function POST({ request }) {
const { name1, name2 } = await request.json();
const combined = `${name1} ${name2}`.trim(); // Remove espaços extras se um dos nomes for vazio
return json(combined);
}
Para submeter dados do navegador para o servidor, as form actions são geralmente a abordagem preferida e mais robusta.
Se um manipulador GET for exportado, requisições HEAD retornarão o content-length do corpo da resposta do manipulador GET.
Manipulador de Fallback
A exportação de um manipulador fallback capturará qualquer método de requisição HTTP não explicitamente tratado, incluindo métodos menos comuns como MOVE.
/// file: src/routes/api/generic-handler/+server.js
import { json, text } from '@sveltejs/kit';
export async function POST({ request }) {
const data = await request.json();
return json({ received: data, method: 'POST' });
}
/** @type {import('./$types').RequestHandler} */
export async function fallback({ request }) {
return text(`Requisição ${request.method} recebida e tratada pelo fallback!`);
}
Para requisições HEAD, o manipulador GET tem precedência sobre o manipulador fallback.
Negociação de Conteúdo
É possível ter arquivos +server.js e +page no mesmo diretório, permitindo que a mesma rota funcione como página e como endpoint de API. O SvelteKit aplica as seguintes regras para determinar qual usar:
- Requisições
PUT,PATCH,DELETEeOPTIONSsão sempre tratadas por+server.js, pois não são aplicáveis a páginas. - Requisições
GET,POSTeHEADsão consideradas requisições de página se o cabeçalhoAcceptpriorizartext/html(típico de navegadores). Caso contrário, são tratadas por+server.js. - Respostas a requisições
GETincluirão o cabeçalhoVary: Acceptpara permitir que proxies e navegadores cacheiem separadamente as respostas HTML e JSON.
Tipagem com $types
Para desenvolvedores TypeScript (ou JavaScript com JSDoc), o SvelteKit gera um arquivo $types.d.ts em um diretório oculto, fornecendo segurança de tipo ao manipular arquivos de rota. Isso inclui tipos para:
PageData(para+page.svelte) eLayoutData(para+layout.svelte) para tipar a propriedadedata.PageLoad,PageServerLoad,LayoutLoadeLayoutServerLoadpara garantir que os parâmetros e valores de retorno das funçõesloadestejam corretamente tipados.
<!--- file: src/routes/blog/[slug]/+page.svelte --->
<script>
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
Ferramentas de IDE para Svelte (como a extensão Svelte for VS Code) podem inferir e inserir esses tipos automaticamente, eliminando a necessidade de escrevê-los manualmente e garantindo a verificação de tipos. O mesmo se aplica ao svelte-check.
Outros Arquivos
Qualquer outro arquivo dentro de um diretório de rota é ignorado pelo SvelteKit. Isso permite que você coloque componentes e módulos de utilidade juntamente com as rotas que os utilizam. Se componentes ou módulos forem necessários por múltiplas rotas, a convenção é armazená-los no diretório $lib.