Deploy Single Page Apps
Connect supports deploying static sites. While intended to support Quarto, R Markdown, and Jupyter documents rendered in a data science development environment, you can also use Connect to serve applications built with JavaScript libraries like React, Vue, or Svelte. This content focuses on an example application where server-side rendering1 is not enabled, as this requires functionality that Connect does not supply.
Familiarity with JavaScript developer tools is assumed. However, you must adapt the steps below based on your preferred library. Depending on the level of interactivity in your application, searching for the terms “static site generation”, “single-page app”, or “client-side routing” may point you to the relevant configuration settings your app will require to run on Connect.
Prerequisites
Before you begin, you must have:
- the public address of the Connect server
- a valid Connect Publisher or Administrator account
- a working
npm
installation
Step 1: Create an example application
From a terminal session, create an example Svelte application:
npx sv create --template demo --no-add-ons --types ts svelte-connect
cd svelte-connect
npm run dev -- --open
Install the static adapter:
npm i -D @sveltejs/adapter-static
Update the configuration to use the static adapter, and add a fallback route:
svelte.config.js
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
export default {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
fallback: "404.html"
})
}; }
Prepare a production build of the application:
npm run build
Step 2: Publish the application to Connect
Using your preferred publishing method, publish the app to Connect:
rsconnect deploy html build --entrypoint build/index.html
::deployApp(
rsconnectappFiles = list.files(path = "build", full.names = TRUE),
appPrimaryDoc="build/index.html"
)
Step 3: Obtain the app GUID
Get the unique identifier for your application, which you will need for the subsequent steps. You can find this unique identifier:
- on the application’s Info tab in the Connect UI
- in the URL returned by the CLI or
rsconnect
Step 4: Modify the application to run on Connect
Update the application configuration:
Configure the base path
Set the base path using the application GUID obtained in Step 3.
svelte.config.js
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
export default {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
fallback: "404.html"
,
})paths: {
base: "/content/<STEP-3-CONTENT-GUID>",
relative: true
}
}; }
If you have configured a custom content URL for your application, set that as the base path instead.
Disable server-side rendering
src/routes/+layout.js
export const ssr = false
Update application URLs to construct correct routes
Import the base
path object and preprend it to all the application URLs:
src/routes/Header.svelte
<script lang="ts">
{ page } from '$app/stores';
import
import logo from '$lib/images/svelte-logo.svg';
import github from '$lib/images/github.svg';{ base } from '$app/paths';
import </script>
<header>
<div class="corner">
<a href="https://svelte.dev/docs/kit">
<img src={logo} alt="SvelteKit" />
</a>
</div>
<nav>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
</svg>
<ul>
<li aria-current={$page.url.pathname === '/' ? 'page' : undefined}>
<a href="{base}/">Home</a>
</li>
<li aria-current={$page.url.pathname === '/about' ? 'page' : undefined}>
<a href="{base}/about">About</a>
</li>
<li aria-current={$page.url.pathname.startsWith('/sverdle') ? 'page' : undefined}>
<a href="{base}/sverdle">Sverdle</a>
</li>
</ul>
<svg viewBox="0 0 2 3" aria-hidden="true">
<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
</svg>
</nav>
Step 5: Redeploy the application
Rebuild the application:
npm run build
Update your initial deployment:
rsconnect deploy html build --entrypoint build/index.html --app-id <STEP-3-CONTENT-GUID>
::deployApp(
rsconnectappFiles = list.files(path = "build", full.names = TRUE),
appPrimaryDoc="build/index.html",
appID="<STEP-3-CONTENT-GUID>"
)
Considerations
If you want your single-page app to call an API served from Connect, it will require an API key. Connect API keys inherit the permissions of their creator. Consider creating a viewer account and assigning it permission to view only the APIs it is intended to access.
Footnotes
See the Rendering on the Web article for a discussion of different rendering approaches: https://web.dev/articles/rendering-on-the-web↩︎