<html lang="en">
<head></head>
<body>

<form id="mainForm" method="post" action="https://stackblitz.com/run" target="_self">
<input type="hidden" name="project[files][README.md]" value="# Hono + Bun Integration for RivetKit

Example project demonstrating Hono web framework with Bun runtime and React frontend integration with [RivetKit](https://rivetkit.org).

[Learn More →](https://github.com/rivet-dev/rivetkit)

[Discord](https://rivet.dev/discord) — [Documentation](https://rivetkit.org) — [Issues](https://github.com/rivet-dev/rivetkit/issues)

## Getting Started

### Prerequisites

- Bun

### Installation

```sh
git clone https://github.com/rivet-dev/rivetkit
cd rivetkit/examples/hono-bun
npm install
```

### Development

```sh
npm run dev
```

This will start both the backend server (on port 8080) and the frontend dev server (on port 5173).

Open your browser to [http://localhost:5173](http://localhost:5173) to see the counter application.

You can also test the server directly by running:

```sh
curl -X POST http://localhost:8080/increment/test
```

## License

Apache 2.0
">
<input type="hidden" name="project[files][package.json]" value="{&quot;name&quot;:&quot;example-hono-bun&quot;,&quot;version&quot;:&quot;2.0.21&quot;,&quot;private&quot;:true,&quot;type&quot;:&quot;module&quot;,&quot;scripts&quot;:{&quot;dev&quot;:&quot;concurrently \&quot;npm run dev:backend\&quot; \&quot;npm run dev:frontend\&quot;&quot;,&quot;dev:backend&quot;:&quot;bun --watch src/backend/server.ts&quot;,&quot;dev:frontend&quot;:&quot;vite&quot;,&quot;build&quot;:&quot;vite build&quot;,&quot;check-types&quot;:&quot;tsc --noEmit&quot;,&quot;connect&quot;:&quot;tsx scripts/connect.ts&quot;},&quot;devDependencies&quot;:{&quot;@types/bun&quot;:&quot;^1.1.15&quot;,&quot;@types/react&quot;:&quot;^18.2.0&quot;,&quot;@types/react-dom&quot;:&quot;^18.2.0&quot;,&quot;@vitejs/plugin-react&quot;:&quot;^4.2.0&quot;,&quot;concurrently&quot;:&quot;^8.2.2&quot;,&quot;rivetkit&quot;:&quot;https://pkg.pr.new/rivet-dev/rivet/rivetkit@1784d8f4835dfe0620f43e819ff860dd7dd02821&quot;,&quot;tsx&quot;:&quot;^3.12.7&quot;,&quot;typescript&quot;:&quot;^5.5.2&quot;,&quot;vite&quot;:&quot;^5.0.0&quot;},&quot;dependencies&quot;:{&quot;@rivetkit/react&quot;:&quot;https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@1784d8f4835dfe0620f43e819ff860dd7dd02821&quot;,&quot;hono&quot;:&quot;^4.7.0&quot;,&quot;react&quot;:&quot;^18.2.0&quot;,&quot;react-dom&quot;:&quot;^18.2.0&quot;},&quot;keywords&quot;:[&quot;rivetkit&quot;,&quot;hono&quot;,&quot;bun&quot;,&quot;actor&quot;,&quot;stateful&quot;,&quot;example&quot;],&quot;stableVersion&quot;:&quot;0.8.0&quot;}">
<input type="hidden" name="project[files][tsconfig.json]" value="{
	&quot;compilerOptions&quot;: {
		/* Visit https://aka.ms/tsconfig.json to read more about this file */

		/* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
		&quot;target&quot;: &quot;esnext&quot;,
		/* Specify a set of bundled library declaration files that describe the target runtime environment. */
		&quot;lib&quot;: [&quot;esnext&quot;, &quot;dom&quot;],
		/* Specify what JSX code is generated. */
		&quot;jsx&quot;: &quot;react-jsx&quot;,

		/* Specify what module code is generated. */
		&quot;module&quot;: &quot;esnext&quot;,
		/* Specify how TypeScript looks up a file from a given module specifier. */
		&quot;moduleResolution&quot;: &quot;bundler&quot;,
		/* Specify type package names to be included without being referenced in a source file. */
		&quot;types&quot;: [&quot;bun&quot;],
		/* Enable importing .json files */
		&quot;resolveJsonModule&quot;: true,

		/* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
		&quot;allowJs&quot;: true,
		/* Enable error reporting in type-checked JavaScript files. */
		&quot;checkJs&quot;: false,

		/* Disable emitting files from a compilation. */
		&quot;noEmit&quot;: true,

		/* Ensure that each file can be safely transpiled without relying on other imports. */
		&quot;isolatedModules&quot;: true,
		/* Allow &#39;import x from y&#39; when a module doesn&#39;t have a default export. */
		&quot;allowSyntheticDefaultImports&quot;: true,
		/* Ensure that casing is correct in imports. */
		&quot;forceConsistentCasingInFileNames&quot;: true,

		/* Enable all strict type-checking options. */
		&quot;strict&quot;: true,

		/* Skip type checking all .d.ts files. */
		&quot;skipLibCheck&quot;: true
	},
	&quot;include&quot;: [&quot;src/**/*&quot;]
}
">
<input type="hidden" name="project[files][turbo.json]" value="{
	&quot;$schema&quot;: &quot;https://turbo.build/schema.json&quot;,
	&quot;extends&quot;: [&quot;//&quot;]
}
">
<input type="hidden" name="project[files][vite.config.ts]" value="import react from &quot;@vitejs/plugin-react&quot;;
import { defineConfig } from &quot;vite&quot;;

export default defineConfig({
	plugins: [react()],
	root: &quot;src/frontend&quot;,
	build: {
		outDir: &quot;../../dist&quot;,
	},
	server: {
		host: &quot;0.0.0.0&quot;,
	},
});
">
<input type="hidden" name="project[files][scripts/connect.ts]" value="import { createClient } from &quot;rivetkit/client&quot;;
import type { Registry } from &quot;../src/registry&quot;;

async function main() {
	const client = createClient&lt;Registry&gt;(&quot;http://localhost:8080/rivet&quot;);

	const counter = client.counter.getOrCreate().connect();

	counter.on(&quot;newCount&quot;, (count: number) =&gt; console.log(&quot;Event:&quot;, count));

	for (let i = 0; i &lt; 5; i++) {
		const out = await counter.increment(5);
		console.log(&quot;RPC:&quot;, out);

		await new Promise((resolve) =&gt; setTimeout(resolve, 1000));
	}

	await new Promise((resolve) =&gt; setTimeout(resolve, 10000));
	await counter.dispose();
}

main();
">
<input type="hidden" name="project[files][src/backend/registry.ts]" value="import { actor, setup } from &quot;rivetkit&quot;;

export const counter = actor({
	state: { count: 0 },
	actions: {
		increment: (c, x: number) =&gt; {
			c.state.count += x;
			c.broadcast(&quot;newCount&quot;, c.state.count);
			return c.state.count;
		},
	},
});

export const registry = setup({
	use: { counter },
});
">
<input type="hidden" name="project[files][src/backend/server.ts]" value="import { Hono } from &quot;hono&quot;;
import { upgradeWebSocket, websocket } from &quot;hono/bun&quot;;
import { cors } from &quot;hono/cors&quot;;
import { registry } from &quot;./registry&quot;;

const { client, fetch } = registry.start({
	basePath: &quot;/rivet&quot;,
	// Hono requires using Hono.serve
	disableDefaultServer: true,
	// Override endpoint
	overrideServerAddress: &quot;http://localhost:8080/rivet&quot;,
	// Specify Hono-specific upgradeWebSocket
	getUpgradeWebSocket: () =&gt; upgradeWebSocket,
});

// Setup router
const app = new Hono();

app.use(
	&quot;*&quot;,
	cors({
		origin: &quot;http://localhost:5173&quot;,
		credentials: true,
	}),
);

app.use(&quot;/rivet/*&quot;, async (c) =&gt; {
	return await fetch(c.req.raw, c.env);
});

// Example HTTP endpoint
app.post(&quot;/increment/:name&quot;, async (c) =&gt; {
	const name = c.req.param(&quot;name&quot;);

	const counter = client.counter.getOrCreate(name);
	const newCount = await counter.increment(1);

	return c.text(`New Count: ${newCount}`);
});

Bun.serve({
	port: 8080,
	fetch: app.fetch,
	websocket,
});

console.log(&quot;Listening at http://localhost:8080&quot;);
">
<input type="hidden" name="project[files][src/frontend/App.tsx]" value="import { createRivetKit } from &quot;@rivetkit/react&quot;;
import { useState } from &quot;react&quot;;
import type { registry } from &quot;../backend/registry&quot;;

const { useActor } = createRivetKit&lt;typeof registry&gt;(&quot;http://localhost:8080/rivet&quot;);

function App() {
	const [count, setCount] = useState(0);
	const [counterName, setCounterName] = useState(&quot;test-counter&quot;);

	const counter = useActor({
		name: &quot;counter&quot;,
		key: [counterName],
	});

	counter.useEvent(&quot;newCount&quot;, (x: number) =&gt; setCount(x));

	const increment = async () =&gt; {
		await counter.connection?.increment(1);
	};

	return (
		&lt;div&gt;
			&lt;h1&gt;Counter: {count}&lt;/h1&gt;
			&lt;input
				type=&quot;text&quot;
				value={counterName}
				onChange={(e) =&gt; setCounterName(e.target.value)}
				placeholder=&quot;Counter name&quot;
			/&gt;
			&lt;button onClick={increment}&gt;Increment&lt;/button&gt;
		&lt;/div&gt;
	);
}

export default App;
">
<input type="hidden" name="project[files][src/frontend/index.html]" value="&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Hono Bun Counter&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
    &lt;script type=&quot;module&quot; src=&quot;/main.tsx&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
">
<input type="hidden" name="project[files][src/frontend/main.tsx]" value="import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import App from &quot;./App&quot;;

ReactDOM.createRoot(document.getElementById(&quot;root&quot;)!).render(
	&lt;React.StrictMode&gt;
		&lt;App /&gt;
	&lt;/React.StrictMode&gt;,
);
">
<input type="hidden" name="project[description]" value="generated by https://pkg.pr.new">
<input type="hidden" name="project[template]" value="node">
<input type="hidden" name="project[title]" value="example-hono-bun">
</form>
<script>document.getElementById("mainForm").submit();</script>

</body></html>