SolidStart
WARNING
At the time of writing SolidStart is still in beta. All of this information is subject to change.
You can use the built-in Vite
virtual module virtual:pwa-register/solid
for SolidStart
.
WARNING
You will need to add workbox-window
as a dev
dependency to your Vite
project.
Getting started
Vite-Plugin-PWA will not register the service worker and inject the manifest automatically in SolidStart. For this, you will need to interface with useRegisterSW
and pwaInfo
directly. This can be achieved easily by making the following changes to your root.tsx
.
/* Add the following imports */
import { useRegisterSW } from "virtual:pwa-register/solid";
import { pwaInfo } from "virtual:pwa-info";
export default function Root() {
// Register the service worker
useRegisterSW({immediate: true});
return (
<Html lang="en">
<Head>
{/* The rest of your Head */}
{/* check for and add a Link for the webmanifest */}
{ pwaInfo?.webManifest?.href ? <Link rel="manifest" href={pwaInfo.webManifest.href}/> : '' }
{/* ..... */}
</Head>
);
}
/* Add the following imports */
import { useRegisterSW } from "virtual:pwa-register/solid";
import { pwaInfo } from "virtual:pwa-info";
export default function Root() {
// Register the service worker
useRegisterSW({immediate: true});
return (
<Html lang="en">
<Head>
{/* The rest of your Head */}
{/* check for and add a Link for the webmanifest */}
{ pwaInfo?.webManifest?.href ? <Link rel="manifest" href={pwaInfo.webManifest.href}/> : '' }
{/* ..... */}
</Head>
);
}
Type declarations
TIP
If your TypeScript build step or IDE complain about not being able to find modules or type definitions on imports, add the following to the compilerOptions.types
array of your tsconfig.json
:
{
"compilerOptions": {
"types": [
"vite-plugin-pwa/client"
]
}
}
{
"compilerOptions": {
"types": [
"vite-plugin-pwa/client"
]
}
}
Or you can add the following reference in any of your d.ts
files (for example, in vite-env.d.ts
or global.d.ts
):
/// <reference types="vite-plugin-pwa/client" />
/// <reference types="vite-plugin-pwa/client" />
declare module 'virtual:pwa-register/solid' {
// @ts-expect-error ignore when solid-js is not installed
import type { Accessor, Setter } from 'solid-js'
export interface RegisterSWOptions {
immediate?: boolean
onNeedRefresh?: () => void
onOfflineReady?: () => void
/**
* Called only if `onRegisteredSW` is not provided.
*
* @deprecated Use `onRegisteredSW` instead.
* @param registration The service worker registration if available.
*/
onRegistered?: (registration: ServiceWorkerRegistration | undefined) => void
/**
* Called once the service worker is registered (requires version `0.12.8+`).
*
* @param swScriptUrl The service worker script url.
* @param registration The service worker registration if available.
*/
onRegisteredSW?: (swScriptUrl: string, registration: ServiceWorkerRegistration | undefined) => void
onRegisterError?: (error: any) => void
}
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: [Accessor<boolean>, Setter<boolean>]
offlineReady: [Accessor<boolean>, Setter<boolean>]
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}
declare module 'virtual:pwa-register/solid' {
// @ts-expect-error ignore when solid-js is not installed
import type { Accessor, Setter } from 'solid-js'
export interface RegisterSWOptions {
immediate?: boolean
onNeedRefresh?: () => void
onOfflineReady?: () => void
/**
* Called only if `onRegisteredSW` is not provided.
*
* @deprecated Use `onRegisteredSW` instead.
* @param registration The service worker registration if available.
*/
onRegistered?: (registration: ServiceWorkerRegistration | undefined) => void
/**
* Called once the service worker is registered (requires version `0.12.8+`).
*
* @param swScriptUrl The service worker script url.
* @param registration The service worker registration if available.
*/
onRegisteredSW?: (swScriptUrl: string, registration: ServiceWorkerRegistration | undefined) => void
onRegisterError?: (error: any) => void
}
export function useRegisterSW(options?: RegisterSWOptions): {
needRefresh: [Accessor<boolean>, Setter<boolean>]
offlineReady: [Accessor<boolean>, Setter<boolean>]
updateServiceWorker: (reloadPage?: boolean) => Promise<void>
}
}
Prompt for update
You can use this ReloadPrompt.tsx
component:
ReloadPrompt.tsx
import type { Component } from 'solid-js'
import { Show } from 'solid-js'
import { useRegisterSW } from 'virtual:pwa-register/solid'
import styles from './ReloadPrompt.module.css'
const ReloadPrompt: Component = () => {
const {
offlineReady: [offlineReady, setOfflineReady],
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW({
onRegistered(r) {
// eslint-disable-next-line prefer-template
console.log('SW Registered: ' + r)
},
onRegisterError(error) {
console.log('SW registration error', error)
},
})
const close = () => {
setOfflineReady(false)
setNeedRefresh(false)
}
return (
<div className={styles.Container}>
<Show when={offlineReady() || needRefresh()}>
<div className={styles.Toast}>
<div className={styles.Message}>
<Show
fallback={<span>New content available, click on reload button to update.</span>}
when={offlineReady()}
>
<span>App ready to work offline</span>
</Show>
</div>
<Show when={needRefresh()}>
<button className={styles.ToastButton} onClick={() => updateServiceWorker(true)}>Reload</button>
</Show>
<button className={styles.ToastButton} onClick={() => close()}>Close</button>
</div>
</Show>
</div>
)
}
export default ReloadPrompt
import type { Component } from 'solid-js'
import { Show } from 'solid-js'
import { useRegisterSW } from 'virtual:pwa-register/solid'
import styles from './ReloadPrompt.module.css'
const ReloadPrompt: Component = () => {
const {
offlineReady: [offlineReady, setOfflineReady],
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW({
onRegistered(r) {
// eslint-disable-next-line prefer-template
console.log('SW Registered: ' + r)
},
onRegisterError(error) {
console.log('SW registration error', error)
},
})
const close = () => {
setOfflineReady(false)
setNeedRefresh(false)
}
return (
<div className={styles.Container}>
<Show when={offlineReady() || needRefresh()}>
<div className={styles.Toast}>
<div className={styles.Message}>
<Show
fallback={<span>New content available, click on reload button to update.</span>}
when={offlineReady()}
>
<span>App ready to work offline</span>
</Show>
</div>
<Show when={needRefresh()}>
<button className={styles.ToastButton} onClick={() => updateServiceWorker(true)}>Reload</button>
</Show>
<button className={styles.ToastButton} onClick={() => close()}>Close</button>
</div>
</Show>
</div>
)
}
export default ReloadPrompt
and its corresponding ReloadPrompt.module.css
styles module:
ReloadPrompt.module.css
.Container {
padding: 0;
margin: 0;
width: 0;
height: 0;
}
.Toast {
position: fixed;
right: 0;
bottom: 0;
margin: 16px;
padding: 12px;
border: 1px solid #8885;
border-radius: 4px;
z-index: 1;
text-align: left;
box-shadow: 3px 4px 5px 0 #8885;
background-color: white;
}
.ToastMessage {
margin-bottom: 8px;
}
.ToastButton {
border: 1px solid #8885;
outline: none;
margin-right: 5px;
border-radius: 2px;
padding: 3px 10px;
}
.Container {
padding: 0;
margin: 0;
width: 0;
height: 0;
}
.Toast {
position: fixed;
right: 0;
bottom: 0;
margin: 16px;
padding: 12px;
border: 1px solid #8885;
border-radius: 4px;
z-index: 1;
text-align: left;
box-shadow: 3px 4px 5px 0 #8885;
background-color: white;
}
.ToastMessage {
margin-bottom: 8px;
}
.ToastButton {
border: 1px solid #8885;
outline: none;
margin-right: 5px;
border-radius: 2px;
padding: 3px 10px;
}
Periodic SW Updates
As explained in Periodic Service Worker Updates, you can use this code to configure this behavior on your application with the virtual module virtual:pwa-register/solid
:
import { useRegisterSW } from 'virtual:pwa-register/solid'
const intervalMS = 60 * 60 * 1000
const updateServiceWorker = useRegisterSW({
onRegistered(r) {
r && setInterval(() => {
r.update()
}, intervalMS)
}
})
import { useRegisterSW } from 'virtual:pwa-register/solid'
const intervalMS = 60 * 60 * 1000
const updateServiceWorker = useRegisterSW({
onRegistered(r) {
r && setInterval(() => {
r.update()
}, intervalMS)
}
})
The interval must be in milliseconds, in the example above it is configured to check the service worker every hour.