;
+}) => (
+
+
{message}
+
+
This is a client component.
+
+);
diff --git a/examples/06_form-demo/src/components/ServerForm.tsx b/examples/06_form-demo/src/components/ServerForm.tsx
new file mode 100644
index 000000000..05b87d1d1
--- /dev/null
+++ b/examples/06_form-demo/src/components/ServerForm.tsx
@@ -0,0 +1,67 @@
+async function submitUserProfile(formData: FormData) {
+ 'use server';
+ const name = formData.get('name');
+ const age = formData.get('age');
+ const favoriteColor = formData.get('favoriteColor');
+ const hobby = formData.get('hobby');
+ const isSubscribed = formData.get('newsletter') === 'on';
+
+ console.log({
+ name,
+ age,
+ favoriteColor,
+ hobby,
+ isSubscribed,
+ });
+}
+
+export const ServerForm = () => {
+ return (
+
+ );
+};
diff --git a/examples/06_form-demo/src/components/footer.tsx b/examples/06_form-demo/src/components/footer.tsx
new file mode 100644
index 000000000..8cfd9c897
--- /dev/null
+++ b/examples/06_form-demo/src/components/footer.tsx
@@ -0,0 +1,18 @@
+export const Footer = () => {
+ return (
+
+ );
+};
diff --git a/examples/06_form-demo/src/components/funcs.ts b/examples/06_form-demo/src/components/funcs.ts
new file mode 100644
index 000000000..822cf91d3
--- /dev/null
+++ b/examples/06_form-demo/src/components/funcs.ts
@@ -0,0 +1,24 @@
+import { readFile, writeFile } from 'node:fs/promises';
+import { unstable_rerenderRoute } from 'waku/router/server';
+
+export const getMessage = async () => {
+ const data = await readFile('./private/message.txt', 'utf8');
+ return data;
+};
+
+export const greet = async (formData: FormData) => {
+ 'use server';
+ // simulate a slow server response
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ const currentData = await getMessage();
+ await writeFile(
+ './private/message.txt',
+ currentData + '\n' + formData.get('name') + ' from server!',
+ );
+ unstable_rerenderRoute('/');
+};
+
+export const increment = async (count: number) => {
+ 'use server';
+ return count + 1;
+};
diff --git a/examples/06_form-demo/src/components/header.tsx b/examples/06_form-demo/src/components/header.tsx
new file mode 100644
index 000000000..13ddf7b5e
--- /dev/null
+++ b/examples/06_form-demo/src/components/header.tsx
@@ -0,0 +1,19 @@
+import { Link } from 'waku';
+
+export const Header = () => {
+ return (
+
+ );
+};
diff --git a/examples/06_form-demo/src/pages/_layout.tsx b/examples/06_form-demo/src/pages/_layout.tsx
new file mode 100644
index 000000000..78f1b0452
--- /dev/null
+++ b/examples/06_form-demo/src/pages/_layout.tsx
@@ -0,0 +1,40 @@
+import '../styles.css';
+
+import type { ReactNode } from 'react';
+
+import { Header } from '../components/header';
+import { Footer } from '../components/footer';
+
+type RootLayoutProps = { children: ReactNode };
+
+export default async function RootLayout({ children }: RootLayoutProps) {
+ const data = await getData();
+
+ return (
+
+
Waku
+
+
+
+
+ {children}
+
+
+
+ );
+}
+
+const getData = async () => {
+ const data = {
+ description: 'An internet website!',
+ icon: '/images/favicon.png',
+ };
+
+ return data;
+};
+
+export const getConfig = async () => {
+ return {
+ render: 'static',
+ } as const;
+};
diff --git a/examples/06_form-demo/src/pages/index.tsx b/examples/06_form-demo/src/pages/index.tsx
new file mode 100644
index 000000000..a049ca53d
--- /dev/null
+++ b/examples/06_form-demo/src/pages/index.tsx
@@ -0,0 +1,24 @@
+import { Form } from '../components/Form';
+import { getMessage, greet } from '../components/funcs';
+import { ServerForm } from '../components/ServerForm';
+
+export default function HomePage() {
+ return (
+
+
+
Server Form
+
+
+
+
Client Form
+
+
+
+ );
+}
+
+export const getConfig = async () => {
+ return {
+ render: 'dynamic',
+ } as const;
+};
diff --git a/examples/06_form-demo/src/styles.css b/examples/06_form-demo/src/styles.css
new file mode 100644
index 000000000..06dc50147
--- /dev/null
+++ b/examples/06_form-demo/src/styles.css
@@ -0,0 +1,5 @@
+@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,400;0,700;1,400;1,700&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@400;700&display=swap');
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/examples/06_form-demo/tailwind.config.js b/examples/06_form-demo/tailwind.config.js
new file mode 100644
index 000000000..b15775b26
--- /dev/null
+++ b/examples/06_form-demo/tailwind.config.js
@@ -0,0 +1,10 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./src/**/*.{js,jsx,ts,tsx}'],
+ theme: {
+ fontFamily: {
+ nunito: ['"Nunito"', 'sans-serif'],
+ 'zen-maru-gothic': ['"Zen Maru Gothic"', 'serif'],
+ },
+ },
+};
diff --git a/examples/06_form-demo/tsconfig.json b/examples/06_form-demo/tsconfig.json
new file mode 100644
index 000000000..33d25f480
--- /dev/null
+++ b/examples/06_form-demo/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "strict": true,
+ "target": "esnext",
+ "downlevelIteration": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "skipLibCheck": true,
+ "noUncheckedIndexedAccess": true,
+ "exactOptionalPropertyTypes": true,
+ "types": ["node", "react/experimental"],
+ "jsx": "react-jsx"
+ }
+}
diff --git a/examples/36_form/src/components/App.tsx b/examples/36_form/src/components/App.tsx
index afb38d4ff..879340765 100644
--- a/examples/36_form/src/components/App.tsx
+++ b/examples/36_form/src/components/App.tsx
@@ -15,8 +15,8 @@ const App = ({ name }: { name: string }) => {
>
Hello {name}!!
This is a server component.
-
+