A +page.server.js file can export actions, which allow you to POST data to the server using the <form> element.
When using <form>, client-side JavaScript is optional, but you can easily progressively enhance your form interactions with JavaScript to provide the best user experience.
The experimental form remote function covers the same use cases as form actions, adding type safety and single-flight mutations. Form actions are feature-complete and will continue to work, but new development is focused on remote functions, which are intended to become the recommended way to communicate with the server. Consider remote functions for new projects, keeping in mind that the API may change while the feature is experimental.
Default actions
In the simplest case, a page declares a default action:
={default(property)default:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO log the user in}};
importtype{Actions(alias)typeActions={[x:string]:Action<Record<string,any>,void|Record<string,any>,string|null>;}importActions}from'./$types';exportconstactionsconstactions:{default:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>;}={default(property)default:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO log the user in}}satisfiesActions(alias)typeActions={[x:string]:Action<Record<string,any>,void|Record<string,any>,string|null>;}importActions;
To invoke this action from the /login page, just add a <form> — no JavaScript needed:
If someone were to click the button, the browser would send the form data via POST request to the server, running the default action.
Actions always use POST requests, since GET requests should never have side-effects.
We can also invoke the action from other pages (for example if there’s a login widget in the nav in the root layout) by adding the action attribute, pointing to the page:
Submitting this form takes you to /login, whether the action succeeds or fails — that’s what the browser does, and use:enhance emulates it. If you’d rather stay where you are and handle the result in place, there’s a recipe for that in the progressive enhancement section.
Named actions
Instead of one default action, a page can have as many named actions as it needs:
={default: async (event) => {login(property)login:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO log the user in},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{// TODO register the user}};
importtype{Actions(alias)typeActions={[x:string]:Action<Record<string,any>,void|Record<string,any>,string|null>;}importActions}from'./$types';exportconstactionsconstactions:{login:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>;register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>;}={default: async (event) => {login(property)login:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO log the user in},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{// TODO register the user}}satisfiesActions(alias)typeActions={[x:string]:Action<Record<string,any>,void|Record<string,any>,string|null>;}importActions;
To invoke a named action, add a query parameter with the name prefixed by a / character:
src/routes/login/+page
<formmethod="POST"action="?/register">
src/routes/+layout
<formmethod="POST"action="/login?/register">
As well as the action attribute, we can use the formaction attribute on a button to POST the same form data to a different action than the parent <form>:
We can’t have default actions next to named actions, because if you POST to a named action without a redirect, the query parameter is persisted in the URL, which means the next default POST would go through the named action from before.
Anatomy of an action
Each action receives a RequestEvent object, allowing you to read the data with request.formData(). After processing the request (for example, logging the user in by setting a cookie), the action can respond with data that will be available through the form property on the corresponding page and through page.form app-wide until the next update.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
value
the cookie value
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
:'/'});return{success(property)success:boolean:true};},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO register the user}};
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
value
the cookie value
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
:'/'});return{success(property)success:boolean:true};},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO register the user}}satisfiesActions(alias)typeActions={[x:string]:Action<Record<string,any>,void|Record<string,any>,string|null>;}importActions;
src/routes/login/+page
<script>/**@type{import('./$types').PageProps}*/let{data,form}=$props();</script>{#ifform?.success}<!-- this message is ephemeral; it exists because the page was rendered inresponse to a form submission. it will vanish if the user reloads --><p>Successfully logged in! Welcome back, {data.user.name}</p>{/if}
<scriptlang="ts">importtype{PageProps}from'./$types';let{data,form}:PageProps=$props();</script>{#ifform?.success}<!-- this message is ephemeral; it exists because the page was rendered inresponse to a form submission. it will vanish if the user reloads --><p>Successfully logged in! Welcome back, {data.user.name}</p>{/if}
Legacy mode
PageProps was added in 2.16.0. In earlier versions, you had to type the data and form properties individually:
In Svelte 4, you’d use export let data and export let form instead to declare properties.
Validation errors
If the request couldn’t be processed because of invalid data, you can return validation errors — along with the previously submitted form values — back to the user so that they can try again. The fail function lets you return an HTTP status code (typically 400 or 422, in the case of validation errors) along with the data. The status code is available through page.status and the data through form:
When using use:enhance (or making a fetch request with the accept: application/json header), the HTTP response status code will match the status code passed to fail. When an action returns data, the response will have status 200, and when it returns nothing (i.e. undefined), the response will have status 204. This makes it easier to track form submission outcomes using observability tools.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
value
the cookie value
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
:'/'});return{success(property)success:boolean:true};},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO register the user}};
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
value
the cookie value
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
:'/'});return{success(property)success:boolean:true};},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO register the user}}satisfiesActions(alias)typeActions={[x:string]:Action<Record<string,any>,void|Record<string,any>,string|null>;}importActions;
Note that as a precaution, we only return the email back to the page — not the password.
src/routes/login/+page
<formmethod="POST"action="?/login">{#ifform?.missing}<pclass="error">The email field is required</p>{/if}{#ifform?.incorrect}<pclass="error">Invalid credentials!</p>{/if}<label>Email<inputname="email"type="email"value={form?.email??''}></label><label>Password<inputname="password"type="password"></label><button>Log in</button><buttonformaction="?/register">Register</button></form>
The returned data must be serializable as JSON. Beyond that, the structure is entirely up to you. For example, if you had multiple forms on the page, you could distinguish which <form> the returned form data referred to with an id property or similar.
Redirects
Redirects (and errors) work exactly the same as in load:
To redirect to an external URL, you must pass { external: true } to allow any external URL except javascript: URLs, or { external: [...] } with an allowlist of permitted origins.
{import('./public.js').Redirect} This error instructs SvelteKit to redirect to the specified location.
{Error} If the provided status is invalid, the location cannot be used as a header value, or the location is an external URL without permission.
Inside query functions (including query.batch and query.live), accessing this property throws an error.
Pass values from the page as arguments to the query instead. Inside form and command functions it relates to the pagethe remote function was called from, not the URL of the endpoint SvelteKit creates for the remote function. Never use itto determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.})=>{constdataconstdata:FormData=awaitrequest(parameter)request:Request
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
value
the cookie value
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
Inside query functions (including query.batch and query.live), accessing this property throws an error.
Pass values from the page as arguments to the query instead. Inside form and command functions it relates to the pagethe remote function was called from, not the URL of the endpoint SvelteKit creates for the remote function. Never use itto determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated..searchParams(property)URL.searchParams:URLSearchParams
The searchParams read-only property of the URL interface returns a URLSearchParams object allowing access to the GET decoded query arguments contained in the URL.
To redirect to an external URL, you must pass { external: true } to allow any external URL except javascript: URLs, or { external: [...] } with an allowlist of permitted origins.
{import('./public.js').Redirect} This error instructs SvelteKit to redirect to the specified location.
{Error} If the provided status is invalid, the location cannot be used as a header value, or the location is an external URL without permission.
Inside query functions (including query.batch and query.live), accessing this property throws an error.
Pass values from the page as arguments to the query instead. Inside form and command functions it relates to the pagethe remote function was called from, not the URL of the endpoint SvelteKit creates for the remote function. Never use itto determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated..searchParams(property)URL.searchParams:URLSearchParams
The searchParams read-only property of the URL interface returns a URLSearchParams object allowing access to the GET decoded query arguments contained in the URL.
('redirectTo'));}return{success(property)success:boolean:true};},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO register the user}};
To redirect to an external URL, you must pass { external: true } to allow any external URL except javascript: URLs, or { external: [...] } with an allowlist of permitted origins.
{import('./public.js').Redirect} This error instructs SvelteKit to redirect to the specified location.
{Error} If the provided status is invalid, the location cannot be used as a header value, or the location is an external URL without permission.
Inside query functions (including query.batch and query.live), accessing this property throws an error.
Pass values from the page as arguments to the query instead. Inside form and command functions it relates to the pagethe remote function was called from, not the URL of the endpoint SvelteKit creates for the remote function. Never use itto determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated.})=>{constdataconstdata:FormData=awaitrequest(parameter)request:Request
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
The get() method of the FormData interface returns the first value associated with a given key from within a FormData object. If you expect multiple values and want all of them, use the getAll() method instead.
Sets a cookie. This will add a set-cookie header to the response, but also make the cookie available via cookies.get or cookies.getAll during the current request.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
value
the cookie value
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
Inside query functions (including query.batch and query.live), accessing this property throws an error.
Pass values from the page as arguments to the query instead. Inside form and command functions it relates to the pagethe remote function was called from, not the URL of the endpoint SvelteKit creates for the remote function. Never use itto determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated..searchParams(property)URL.searchParams:URLSearchParams
The searchParams read-only property of the URL interface returns a URLSearchParams object allowing access to the GET decoded query arguments contained in the URL.
To redirect to an external URL, you must pass { external: true } to allow any external URL except javascript: URLs, or { external: [...] } with an allowlist of permitted origins.
{import('./public.js').Redirect} This error instructs SvelteKit to redirect to the specified location.
{Error} If the provided status is invalid, the location cannot be used as a header value, or the location is an external URL without permission.
Inside query functions (including query.batch and query.live), accessing this property throws an error.
Pass values from the page as arguments to the query instead. Inside form and command functions it relates to the pagethe remote function was called from, not the URL of the endpoint SvelteKit creates for the remote function. Never use itto determine whether or not a user is authorized to access certain data, as these values are part of the request which could be manipulated..searchParams(property)URL.searchParams:URLSearchParams
The searchParams read-only property of the URL interface returns a URLSearchParams object allowing access to the GET decoded query arguments contained in the URL.
('redirectTo'));}return{success(property)success:boolean:true};},register(property)register:(event:RequestEvent<Record<string,any>,string|null>)=>Promise<void>:async(event(parameter)event:RequestEvent<Record<string,any>,string|null>)=>{ // TODO register the user}}satisfiesActions(alias)typeActions={[x:string]:Action<Record<string,any>,void|Record<string,any>,string|null>;}importActions;
Loading data
After an action runs, the page will be re-rendered (unless a redirect or an unexpected error occurs), with the action’s return value available to the page as the form prop. This means that your page’s load functions will run after the action completes.
Note that handle runs before the action is invoked, and does not rerun before the load functions. This means that if, for example, you use handle to populate event.locals based on a cookie, you must update event.locals when you set or delete the cookie in an action:
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
The httpOnly is true by default, as is secure, except during development, when it defaults to false. These must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP.
The path option is '/' by default. You can use relative paths, or set path: '' to make the cookie only available on the current path and its children.
name
the name of the cookie
opts
the options passed to cookie.stringifySetCookie with the SvelteKit defaults described above. See documentation here
In the preceding sections we built a /login action that works without client-side JavaScript — not a fetch in sight. That’s great, but when JavaScript is available we can progressively enhance our form interactions to provide a better user experience.
use:enhance
The easiest way to progressively enhance a form is to add the use:enhance action:
use:enhance can only be used with forms that have method="POST" and point to actions defined in a +page.server.js file. It will not work with method="GET", which is the default for forms without a specified method. Attempting to use use:enhance on forms without method="POST" or posting to a +server.js endpoint will result in an error.
Yes, it’s a little confusing that the enhance action and <form action> are both called ‘action’. These docs are action-packed. Sorry.
Without an argument, use:enhance will emulate the browser-native behaviour, just without the full-page reloads. It will:
if the action is on the page you’re submitting from, emulate a reload of the current page — updating the form property, page.form and page.status on a successful or invalid response
if the action is on a different page (for example <form action="/somewhere/else" ..>), navigate to the action’s URL on a successful or invalid response, populating that page’s form property and page.status, just as a native submission would. A new history entry is pushed
reset the <form> element
refresh all data using refreshAll on a successful response
call goto on a redirect response
render the nearest +error boundary if an unexpected error occurs — the boundary nearest the action’s route, if the action is on a different page
Where a submission lands is result.location — the form’s action with the ?/actionName parameter removed, since that parameter has done its job once the action has run. action="/login?/do-login" lands on /login, and any other query parameters are kept, so action="/login?redirectTo=/dashboard&/do-login" lands on /login?redirectTo=/dashboard. The destination’s load functions see that URL via url.search.
Two consequences worth knowing:
A relative action replaces the entire query string as a side effect of URL resolution — action="?/delete" on /items?page=2 resolves to /items?/delete, so both enhanced and native submissions drop page=2. Write action="?page=2&/delete" to preserve it.
Without JavaScript the browser can’t strip anything, so an unenhanced submission leaves you on the raw action URL (/login?/do-login). Reloading that URL is a plain GET, which doesn’t re-run the action and renders the page normally, so the difference is cosmetic — but a load function can observe a different url.search depending on whether JavaScript is available.
If you don’t want a cross-page submission to navigate — a common need for login or newsletter widgets that live in a layout and post to another page’s action — pass navigate: false to update, which gives you the same behaviour as SvelteKit 2:
The result is applied to the page you’re on: form, page.form and page.status are updated, and error results render the current route’s nearest +error boundary.
Customising use:enhance
To customise the behaviour, you can provide a SubmitFunction that runs immediately before the form is submitted, and (optionally) returns a callback that runs with the ActionResult.
<formmethod="POST"use:enhance={({formElement,formData,action,cancel,submitter})=>{// `formElement` is this `<form>` element// `formData` is its `FormData` object that's about to be submitted// `action` is the URL to which the form is posted// calling `cancel()` will prevent the submission// `submitter` is the `HTMLElement` that caused the form to be submittedreturnasync({result,update})=>{// `result` is an `ActionResult` object// `update` is a function which triggers the default logic that would be triggered if this callback wasn't set};}}>
You can use these functions to show and hide loading UI, and so on.
If you return a callback, you override the default post-submission behavior. To get it back, call update, which accepts navigate, refreshAll and reset parameters, or use applyAction on the result:
src/routes/login/+page
<script>import{enhance,applyAction}from'$app/forms';/**@type{import('./$types').PageProps}*/let{form}=$props();</script><formmethod="POST"use:enhance={({formElement,formData,action,cancel})=>{returnasync({result})=>{// `result` is an `ActionResult` objectif(result.type==='redirect'){goto(result.location);}else{awaitapplyAction(result);}};}}>
<scriptlang="ts">import{enhance,applyAction}from'$app/forms';importtype{PageProps}from'./$types';let{form}:PageProps=$props();</script><formmethod="POST"use:enhance={({formElement,formData,action,cancel})=>{returnasync({result})=>{// `result` is an `ActionResult` objectif(result.type==='redirect'){goto(result.location);}else{awaitapplyAction(result);}};}}>
update accepts these options:
reset: false if you don’t want the <form> values to be reset after a successful submission
refreshAll controls whether all data is refreshed after submission. It defaults to true for successful results and false for failures. When the submission navigates to another page, setting it to false does not prevent the destination’s own load functions from running — it only allows shared layout data to be reused. invalidateAll is a deprecated alias for refreshAll
navigate: false applies a non-redirect result to the current page instead of navigating to result.location. Redirects are always followed
The behaviour of applyAction(result) depends on result.type:
success, failure — sets page.status to result.status and updates form and page.form to result.data
error — renders the current route’s nearest +error boundary with result.error
In all cases, focus will be reset. applyAction does not navigate to the location of a non-redirect result or refresh data. Use update to get the complete default enhanced behavior, including carrying form data and status into a cross-page navigation. When implementing enhancement manually, inspect result.location and use goto or refreshAll as appropriate.
Custom event listener
We can also implement progressive enhancement ourselves, without use:enhance, with a normal event listener on the <form>:
Note that you need to deserialize the response before processing it further using the corresponding method from $app/forms. JSON.parse() isn’t enough because form actions - like load functions - also support returning Date or BigInt objects.
If you have a +server.js alongside your +page.server.js, fetch requests will be routed there by default. To POST to an action in +page.server.js instead, use the custom x-sveltekit-action header:
A Headers object, an object literal, or an array of two-item arrays to set request's headers.
:{'x-sveltekit-action':'true'}});
Alternatives
Form actions are the preferred way to send data to the server, since they can be progressively enhanced, but you can also use +server.js files to expose (for example) a JSON API. Here’s how such an interaction could look like:
importtype{RequestHandler(alias)typeRequestHandler=(event:RequestEvent<Record<string,any>,string|null>)=>MaybePromise<Response>importRequestHandler}from'./$types';exportconstPOSTconstPOST:RequestHandler:RequestHandler(alias)typeRequestHandler=(event:RequestEvent<Record<string,any>,string|null>)=>MaybePromise<Response>importRequestHandler=()=>{ // do something};
GET vs POST
As we’ve seen, to invoke a form action you must use method="POST".
Some forms don’t need to POST data to the server — search inputs, for example. For these you can use method="GET" (or, equivalently, no method at all), and SvelteKit will treat them like <a> elements, using the client-side router instead of a full page navigation:
Submitting this form will navigate to /search?q=... and invoke your load function but will not invoke an action. As with <a> elements, you can set the data-sveltekit-reload, data-sveltekit-replacestate and data-sveltekit-reset attributes on the <form> to control the router’s behaviour.