3This val can be used in other vals to send notifications to a segment using [OneSignal's REST API](https://documentation.onesignal.com/reference/create-notification)
4
5This is really handy if you want to send push notifications to your phone without building a native app! I built a barebones React PWA that asks for a password then loads the OneSignal Web SDK that I deployed to [Netlify](https://www.netlify.com/) for free. OneSignal has [easy to follow docs](https://documentation.onesignal.com/docs/web-sdk-setup) so you can build this functionality into a React, Angular, Vue app or even Wordpress! Then [install the PWA](https://www.bitcot.com/how-to-install-a-pwa-to-your-device/) on your platform of choice and you're off to the races!
6
7## Setup
1/** @jsxImportSource npm:react **/
2import { fetchText } from "https://esm.town/v/stevekrouse/fetchText?v=6";
3import { load } from "npm:cheerio";
4import { renderToString } from "npm:react-dom@18/server";
5
6export default async (req: Request) => {
5 type PersistentState,
6 persistentStateLazy,
7 persistentStateReactive,
8} from "https://esm.town/v/postpostscript/persistentState";
9import { blob } from "https://esm.town/v/std/blob";
17};
18
19export async function reactiveStateBlob<T extends Record<string, unknown>>(
20 getDefault: PersistentStateGetDefault<T>,
21 opts?: {
28 const _key = opts?.key ?? (() => {
29 const { handle, name } = rootValRef();
30 return `reactiveStateBlob:${handle}/${name}`;
31 })();
32
38 : undefined;
39
40 const cache = persistentStateBlobReactive({
41 ...opts,
42 key: _key,
65}
66
67export function persistentStateBlobReactive<T extends Record<string, unknown>>(
68 opts: PersistentStateBlobOptions<T>,
69): PersistentState<T> {
70 return persistentStateReactive(persistentStateBlob(opts));
71}
72
1// JSX can be used in the client val thanks to this magic comment
2/** @jsxImportSource https://esm.sh/react **/
3// Make sure to only import from esm.sh (npm: specifier are not supported in the browser)
4import { motion } from "https://esm.sh/framer-motion";
5import React from "https://esm.sh/react";
6import ReactDOM from "https://esm.sh/react-dom";
7// import "https://esm.sh/tldraw/tldraw.css";
8
24
25// The app will be rendered inside the root div
26const root = ReactDOM.createRoot(document.getElementById("root"));
27root.render(<App />);
1/**
2 * @title Running React on the Client
3 * @description Vals can also be used to host client-side code!
4 * @preview https://pomdtr-react_example_server.web.val.run
5 * @include pomdtr/react_example_client
6 * @resource [React - Quick Start](https://react.dev/learn)
7 */
8
12 `<html>
13 <head>
14 <title>React Example</title>
15 <meta name="viewport" content="width=device-width, initial-scale=1" />
16 <style>
60 <body>
61 <main id="root"></main>
62 <script type="module" src="https://esm.town/v/tfayyaz/react_framer_motion_script"></script/>
63 </body>
64 </html>`,
1/**
2 * @title Running React on the Client
3 * @description Vals can also be used to host client-side code!
4 * @preview https://pomdtr-react_example_server.web.val.run
5 * @include pomdtr/react_example_client
6 * @resource [React - Quick Start](https://react.dev/learn)
7 */
8
12 `<html>
13 <head>
14 <title>React Example</title>
15 <meta name="viewport" content="width=device-width, initial-scale=1" />
16 <style>
21 <body>
22 <main id="root"></main>
23 <script type="module" src="https://esm.town/v/andreterron/react_tldraw_client"></script/>
24 </body>
25 </html>`,
1// JSX can be used in the client val thanks to this magic comment
2/** @jsxImportSource https://esm.sh/react **/
3// Make sure to only import from esm.sh (npm: specifier are not supported in the browser)
4import React from "https://esm.sh/react";
5import ReactDOM from "https://esm.sh/react-dom";
6import { Tldraw } from "https://esm.sh/tldraw";
7
18
19// The app will be rendered inside the root div
20const root = ReactDOM.createRoot(document.getElementById("root"));
21root.render(<App />);
1// JSX can be used in the client val thanks to this magic comment
2/** @jsxImportSource https://esm.sh/react **/
3// Make sure to only import from esm.sh (npm: specifier are not supported in the browser)
4import React from "https://esm.sh/react";
5import ReactDOM from "https://esm.sh/react-dom";
6import { Button } from "https://esm.town/v/nbbaier/shadcnButton";
7import {
53
54// The app will be rendered inside the root div
55const root = ReactDOM.createRoot(document.getElementById("root"));
56root.render(<App />);
1/** @jsxImportSource npm:preact */
2import { RenderFunc } from "https://esm.town/v/pomdtr/invoice_schema";
3import clsx from "npm:clsx";
4import { render as preactRender } from "npm:preact-render-to-string";
5
6export const render: RenderFunc = ({ title, table, to, from, details }) => {
7 return preactRender(
8 <html>
9 <head>
1# SSR React Mini & SQLite Todo App
2
3This Todo App is server rendered *and* client-hydrated React. This architecture is a lightweight alternative to NextJS, RemixJS, or other React metaframeworks with no compile or build step. The data is saved server-side in [Val Town SQLite](https://docs.val.town/std/sqlite/).
4
5
6
7## SSR React Mini Framework
8
9This ["framework"](https://www.val.town/v/stevekrouse/ssr_react_mini) is currently 44 lines of code, so it's obviously not a true replacement for NextJS or Remix.
10
11The trick is [client-side importing](https://www.val.town/v/stevekrouse/ssr_react_mini?v=53#L30) the React component that you're [server rendering](https://www.val.town/v/stevekrouse/ssr_react_mini?v=53#L35-37). Val Town is uniquely suited for this trick because it both runs your code server-side and exposes vals as [modules](https://esm.town/v/stevekrouse/TodoApp) importable by the browser.
12
13The tricky part is making sure that server-only code doesn't run on the client and vice-versa. For example, because this val colocates the server-side `loader` and `action` with the React component we have to be careful to do all server-only imports (ie sqlite) dynamically inside the [`loader`](https://www.val.town/v/stevekrouse/TodoApp?v=246#L7) and [`action`](https://www.val.town/v/stevekrouse/TodoApp?v=246#L20), so they only run server-side.
14