Skip to content

Commit 2070e50

Browse files
Restore inline-web-view migration guide accidentally removed with blocks cleanup
The file was swept up in the blocks removal (PR #85) but is a needed migration guide for useWebView -> Devvit Web. Restores both main docs and versioned (0.12) copies, plus sidebar entries in both sidebars. Made-with: Cursor
1 parent 83eab95 commit 2070e50

4 files changed

Lines changed: 406 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Migrating from useWebView to Devvit Web
2+
3+
This guide will migrate your legacy webview implementation (using useWebView inside of Blocks) to the official Devvit Web setup.
4+
5+
:::note
6+
Apps can be partially migrated, you don't need to re-write everything!
7+
:::
8+
9+
# Before
10+
11+
- Use postMessage for message passing
12+
- App logic is isomorphic (server/client) in Blocks
13+
- No client effects available
14+
15+
# After
16+
17+
- No postMessage required
18+
- Use web native fetch() to server endpoints directly
19+
- App logic is either on the client, or the server, with clear deliniation
20+
- Client effects are available directly from web views
21+
22+
## Setting up devvit.json
23+
24+
The first thing you need to do is setup `devvit.json`.
25+
26+
Schema here: https://developers.reddit.com/schema/config-file.v1.json
27+
28+
`devvit.json` supports all capabilities previously available in the `Devvit` singleton, e.g. `Devvit.addCustomPostType()`. For the purposes of this guide, only the post rendering logic will be migrated.
29+
30+
### Understanding entrypoints
31+
32+
Your `devvit.json` must have entrypoints that point to **outputs** of your code. It is assumed that you have installed a bundler or can otherwise prepare static assets to appear in your dist folders.
33+
34+
```js
35+
{
36+
"post": {
37+
"client": { // The output of your client app, probably /src/webroot
38+
"dir": "dist/client",
39+
"entry": "dist/client/index.html"
40+
}
41+
},
42+
"blocks": { // point to where you export Devvit singleton, probably src/main.tsx
43+
"entry": "src/devvit/main.tsx"
44+
},
45+
"server": { // new folder which will contain your Node server
46+
"entry": "dist/server/index.cjs"
47+
},
48+
}
49+
```
50+
51+
> You'll notice that the `blocks` entrypoint points to your TypeScript source file (`src/devvit/main.tsx`). This is because the Devvit CLI handles bundling for Blocks automatically. For your `client` and `server` entrypoints, however, you are responsible for bundling your code and pointing to the final output files in your `dist` directory.
52+
53+
### Building your client and server
54+
55+
The `devvit.json` configuration for `client` and `server` points to files in a `dist` directory. This means you're responsible for building your web and server assets. You can use any bundler you like, such as `vite`.
56+
57+
For example, your `package.json` might include scripts to output your assets to the `dist` folder.
58+
59+
Sample server vite config
60+
61+
```ts title="src/server/vite.config.ts
62+
import { defineConfig } from 'vite';
63+
import { builtinModules } from 'node:module';
64+
65+
export default defineConfig({
66+
ssr: {
67+
noExternal: true,
68+
},
69+
build: {
70+
ssr: 'index.ts',
71+
outDir: '../../dist/server',
72+
target: 'node22',
73+
sourcemap: true,
74+
rollupOptions: {
75+
external: [...builtinModules],
76+
77+
output: {
78+
format: 'cjs',
79+
entryFileNames: 'index.cjs',
80+
inlineDynamicImports: true,
81+
},
82+
},
83+
},
84+
});
85+
```
86+
87+
Sample client Vite config (for React)
88+
89+
```ts title="src/client/vite.config.ts
90+
import { defineConfig } from 'vite';
91+
import tailwind from '@tailwindcss/vite';
92+
import react from '@vitejs/plugin-react';
93+
94+
// https://vitejs.dev/config/
95+
export default defineConfig({
96+
plugins: [react(), tailwind()],
97+
build: {
98+
outDir: '../../dist/client',
99+
sourcemap: true,
100+
chunkSizeWarningLimit: 1500,
101+
},
102+
});
103+
```
104+
105+
## Setting up your server endpoints
106+
107+
You can use any Node server for your server endpoints. This guide will use [Express](https://expressjs.com/).
108+
109+
1. Install Express
110+
111+
```
112+
npm i express
113+
```
114+
115+
2. Create a server index file
116+
117+
```ts title='src/server/index.ts'
118+
import express from 'express';
119+
// The `@devvit/server` package provides the tools to create a server,
120+
// and gives you access to the request context.
121+
import { createServer, context, getServerPort, redis } from '@devvit/web/server';
122+
123+
const app = express();
124+
125+
// Middleware for JSON body parsing
126+
app.use(express.json());
127+
// Middleware for URL-encoded body parsing
128+
app.use(express.urlencoded({ extended: true }));
129+
// Middleware for plain text body parsing
130+
app.use(express.text());
131+
132+
const router = express.Router();
133+
134+
// The `context` object is automatically populated with useful information,
135+
// like the current user's ID. Devvit's services, like redis, are also
136+
// available via named imports from `@devvit/server`.
137+
router.get<{ postId: string }, { message: string }>(
138+
'/api/hello',
139+
async (_req, res): Promise<void> => {
140+
const { userId } = context;
141+
res.status(200).json({
142+
message: `Hello ${userId}`,
143+
});
144+
}
145+
);
146+
147+
router.get('/api/init', async (_req, res): Promise<void> => {
148+
res.json({ initialState: await redis.get('initialState') });
149+
});
150+
151+
// Use router middleware
152+
app.use(router);
153+
154+
// Get port from environment variable with fallback
155+
const port = getServerPort();
156+
157+
const server = createServer(app);
158+
server.on('error', (err) => console.error(`server error; ${err.stack}`));
159+
server.listen(port, () => console.log(`http://localhost:${port}`));
160+
```
161+
162+
### Calling your server endpoints
163+
164+
Now
165+
166+
Instead of using `postMessage`, your client-side code can now directly fetch the initial state from the `/api/init` endpoint we defined in the server.
167+
168+
```ts title=/src/client/app.ts
169+
const res = await fetch('/api/init');
170+
const data = await res.json();
171+
console.log(data.initialState); // Logs the state from Redis
172+
```
173+
174+
## Client effects
175+
176+
Previously, client effects were not available to your webview app. You had to pass a custom postMessage and handle that message in Blocks. Now, all client effects are available directly in the web-view through `@devvit/client`.
177+
178+
Before
179+
180+
```ts title=/src/devvit/main.tsx
181+
const BlocksComponent = () => {
182+
const wv = useWebView({
183+
onMessage: (message) => {
184+
if (message.type === 'navigate_to') {
185+
ui.navigateTo(message.data.destination);
186+
}
187+
},
188+
});
189+
};
190+
```
191+
192+
```js title=webroot/app.js
193+
window.postMessage({ type: 'navigate_to', destination: 'reddit.com' });
194+
```
195+
196+
Now
197+
198+
```ts title=client/app.ts
199+
import { navigateTo } from '@devvit/web/client';
200+
201+
navigateTo('reddit.com');
202+
```

sidebars.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ const sidebars: SidebarsConfig = {
294294
type: "category",
295295
label: "Migration Guides",
296296
items: [
297+
"guides/migrate/inline-web-view",
297298
{
298299
type: "category",
299300
label: "Splash Screens",

0 commit comments

Comments
 (0)