Skip to content

Views

Before rendering views you need to assign a rendering function to your server.
This rendering function is used to render views when invoking send.View().

You can choose between two types of rendering functions: csr and ssr.

The csr function simply reads the contents of your index.html, injects the view properties into the document and returns the result.

You can create a csr function using csr.New().

main.go
package main
import (
"embed"
"main/lib/core/server"
"main/lib/core/view/csr"
"os"
)
//go:embed app/dist
var efs embed.FS
var srv = server.New()
var dev = os.Getenv("DEV") == "1"
var render = csr.New(ssr.Config{ Efs: efs, Disk: dev })
func main() {
defer server.Start(srv)
srv.Render = render
srv.Efs = efs
}

The ssr function uses a JavaScript engine in order to render svelte components on the fly and returns the result.

You can create an ssr function using ssr.New().

main.go
package main
import (
"embed"
"main/lib/core/server"
"main/lib/core/view/ssr"
"os"
)
//go:embed app/dist
var efs embed.FS
var srv = server.New()
var dev = os.Getenv("DEV") == "1"
var render = ssr.New(ssr.Config{Efs: efs, Disk: dev})
func main() {
defer server.Start(srv)
srv.Render = render
srv.Efs = efs
}

Views are svelte components exported by app/exports.server.ts and/or app/exports.client.ts.

  • Directoryapp
    • exports.client.ts
    • exports.server.ts

Views that are meant to be rendered on the server should be exported by app/exports.server.ts.

app/exports.server.ts
import Welcome from '$lib/views/Welcome.svelte'
import Profile from '$lib/views/Profile.svelte'
export const views = { // Defines that components "Welcome" and "Profile"
// can be rendered on the server when sent with send.View().
"Welcome": Welcome,
"Profile": Profile,
}

Views that are meant to be rendered on the client should be exported by app/exports.client.ts.

app/exports.client.ts
export const views = { // Defines that components "Welcome" and "Profile"
// can be bundled and rendered on the client when
// sent with send.View().
"Welcome": import('$lib/views/Welcome.svelte'),
"Profile": import('$lib/views/Profile.svelte'),
}

These views are being imported asynchronously in order to split them in different bundles, however you can simply create fake promises in order to bundle them all together and eliminate network latency when transitioning between view.

app/exports.client.ts
import Welcome from '$lib/views/Welcome.svelte'
import Profile from '$lib/views/Profile.svelte'
export const views = {
"Welcome": Promise.resolve(Welcome),
"Profile": Promise.resolve(Profile),
}

Use send.View() to send a view.

lib/routes/handlers/welcome/view.go
package lib
import (
"main/lib/core/client"
"main/lib/core/send"
"main/lib/core/view"
)
func View(c *client.Client) {
send.View(c, view.View{Name: "Welcome"}) // Sends view "Welcome".
}

The Name of the view will be used to lookup the view component exported by app/exports.server.ts and/or app/exports.client.ts.

There is no way to specify a “default view”.

However, you can use send.FileOrElse() in order to send the requested file or run custom logic if it doesn’t exist.

lib/routes/handlers/welcome/view.go
package lib
import (
"main/lib/core/client"
"main/lib/core/send"
"main/lib/core/view"
)
func View(c *client.Client) {
send.FileOrElse(c, func () { // Attempts to send requested file, or else...
send.View(c, view.View{Name: "Welcome"}) // ...sends view "Welcome".
})
}

Usually you would map this handler to the default GET / pattern, which automatically captures all unmatched requests.

package main
import (
"embed"
"main/lib/core/client"
"main/lib/core/server"
)
//go:embed app/dist
var efs embed.FS
var srv = server.Default(efs) // Creates server config.
var route = route.Route{ // Creates route.
Pattern: "GET /", // Sets route pattern.
Handler: welcome.View, // Sets route handler.
}
func main() {
defer server.Start(srv) // Starts the server.
srv.Routes = append(srv.Routes, route) // Adds the route to the server.
}

Optionally, you can specify properties for your View with the Props field.

lib/routes/handlers/welcome/view.go
package lib
import (
"main/lib/core/client"
"main/lib/core/send"
"main/lib/core/view"
)
func View(c *client.Client) {
send.View(c, view.View{ // Sends view.
Name: "Welcome", // Sets view name.
Props: map[string]string{ // Sets view props, which will be injected into the svelte component.
"name": "world", // Adds property "name" with value "world".
},
})
}

These properties are passed down to your view component.

app/lib/views/Hello.svelte
<script lang="ts">
type Props = { name: string }
let {name}:Props = $props() // Retrieves view props.
</script>
<h1>Hello {name}</h1>

You can choose how to render views with the RenderMode property.

Using RenderFull, the view is rendered on both the server and the client.
This is the default mode.

lib/routes/handlers/welcome/view.go
package lib
import (
"main/lib/core/client"
"main/lib/core/send"
"main/lib/core/view"
)
func View(c *client.Client) {
send.View(c, view.View{ // Sends view.
Name: "Welcome", // Sets view name.
RenderMode: view.RenderModeFull, // Renders view on server and client.
})
}

Using RenderServer, the view is rendered only on the server.
You’ll have to deal away with apis such as fetch; your new best friend is form.

lib/routes/handlers/welcome/view.go
package lib
import (
"main/lib/core/client"
"main/lib/core/send"
"main/lib/core/view"
)
func View(c *client.Client) {
send.View(c, view.View{ // Sends view.
Name: "Welcome", // Sets view name.
RenderMode: view.RenderModeServer, // Renders view only on server.
})
}

Using RenderClient, the view is rendered only on the client by loading a JavaScript bundle asynchronously.

lib/routes/handlers/welcome/view.go
package lib
import (
"main/lib/core/client"
"main/lib/core/send"
"main/lib/core/view"
)
func View(c *client.Client) {
send.View(c, view.View{ // Sends view.
Name: "Welcome", // Sets view name.
RenderMode: view.RenderModeClient, // Renders view only on client.
})
}

Since the ssr function supports all rendering modes, you might ask yourself why bother using a csr function at all?

When using a csr function, the server-side JavaScript engine is completely eliminated from your final binary, reducing its size from a minimum of 24MB, to a minimum of 10MB.