Skip to content

Commit 29dd2ee

Browse files
committed
chore: add flask-react project sample
Signed-off-by: Dario Valdespino <dvaldespino00@gmail.com>
1 parent 66033d6 commit 29dd2ee

9 files changed

Lines changed: 369 additions & 28 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.dev/
2+
!.dev/elide.lock*
3+
node_modules
4+
node_modules/
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from elide import Flask, react, request
2+
3+
# create and configure an app
4+
app = Flask(__name__)
5+
home = react("index.tsx")
6+
7+
# basic app routing
8+
@app.route("/")
9+
def hello_world():
10+
user_agent = request.headers["User-Agent"]
11+
return home(user_agent = user_agent)
12+
13+
print("Starting server worker at http://localhost:3000")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
amends "elide:project.pkl"
2+
3+
name = "flask-react"
4+
5+
dependencies {
6+
pip {
7+
packages { "flask" }
8+
}
9+
npm {
10+
packages {
11+
"react@18.3.1"
12+
"react-dom@18.3.1"
13+
}
14+
}
15+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { renderToString } from "react-dom/server";
2+
import React from "react";
3+
console.log(process.env);
4+
type SnippetKey = "bash" | "python" | "tsx";
5+
6+
const BASH_SNIPPET = `$ elide app.py`;
7+
8+
const PYTHON_SNIPPET = `
9+
from elide import Flask, react, request
10+
11+
# create and configure an app
12+
app = Flask(__name__)
13+
home = react("index.tsx")
14+
15+
# basic app routing
16+
@app.route("/")
17+
def hello_world():
18+
user_agent = request.headers["User-Agent"]
19+
return home(user_agent=user_agent)
20+
`.trim();
21+
22+
const REACT_SNIPPET = `
23+
export default async function render(props): Promise<string> {
24+
return renderToString(
25+
<App
26+
nowISO={new Date().toISOString()}
27+
ua={props.user_agent ?? ""}
28+
reqId={Date.now()}
29+
pid={process.pid}
30+
/>,
31+
);
32+
}
33+
`.trim();
34+
35+
const SNIPPETS: Array<{
36+
key: SnippetKey;
37+
title: string;
38+
subtitle: string;
39+
language: string;
40+
code: string;
41+
}> = [
42+
{
43+
key: "bash",
44+
title: "Run in one line",
45+
subtitle:
46+
"Launch the server without any additional steps: no virtual environments, no special commands",
47+
language: "bash",
48+
code: BASH_SNIPPET,
49+
},
50+
{
51+
key: "python",
52+
title: "Use your existing Python server code",
53+
subtitle: "Serve React markup from the Flask apps you already have",
54+
language: "python",
55+
code: PYTHON_SNIPPET,
56+
},
57+
{
58+
key: "tsx",
59+
title: "Tap into the React ecosystem",
60+
subtitle: "Render React components server-side with Elide",
61+
language: "typescript",
62+
code: REACT_SNIPPET,
63+
},
64+
];
65+
66+
function App(props: {
67+
nowISO: string;
68+
ua: string;
69+
reqId: string;
70+
pid: number;
71+
}) {
72+
return (
73+
<html lang="en">
74+
<head>
75+
<meta charSet="utf-8" />
76+
<meta name="viewport" content="width=device-width, initial-scale=1" />
77+
<title>Pure React SSR · No Client JS</title>
78+
<link rel="preconnect" href="https://fonts.googleapis.com" />
79+
<link
80+
rel="preconnect"
81+
href="https://fonts.gstatic.com"
82+
crossOrigin=""
83+
/>
84+
<link
85+
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Roboto:wght@400;500;700&family=Roboto+Condensed:wght@700&display=swap"
86+
rel="stylesheet"
87+
/>
88+
<link rel="stylesheet" href="/static/styles.css" />
89+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
90+
<script>hljs.highlightAll();</script>
91+
</head>
92+
<body>
93+
<header>
94+
<a className="hero" href="/" aria-label="Home">
95+
<img
96+
className="logo"
97+
alt="Elide logo"
98+
src="/static/elide-logo.svg"
99+
/>
100+
<span>Powered by Elide</span>
101+
</a>
102+
<h1>React SSR + Flask delivered by Elide</h1>
103+
<p className="tagline">
104+
This app renders entirely on the server, it is written using Flask
105+
and is served by Elide's new polyglot HTTP engine
106+
</p>
107+
</header>
108+
109+
<main>
110+
<section className="grid">
111+
<div className="card">
112+
<h2>Request information</h2>
113+
<p className="lead">
114+
This section is generated on the server for each request
115+
</p>
116+
<ul>
117+
<li>
118+
Render time: <strong>{props.nowISO}</strong>
119+
</li>
120+
<li>
121+
Request ID: <strong>{props.reqId}</strong>
122+
</li>
123+
<li>
124+
Process PID: <strong>{props.pid}</strong>
125+
</li>
126+
<li>
127+
User-Agent: <strong>{props.ua || "(unknown)"}</strong>
128+
</li>
129+
</ul>
130+
</div>
131+
</section>
132+
133+
<section className="code-sections">
134+
{SNIPPETS.map((snippet) => (
135+
<div className="code-card" key={snippet.key}>
136+
<h3>{snippet.title}</h3>
137+
<p className="snippet-subtitle">{snippet.subtitle}</p>
138+
<pre>
139+
<code className={`language-${snippet.language}`}>
140+
{snippet.code}
141+
</code>
142+
</pre>
143+
</div>
144+
))}
145+
</section>
146+
</main>
147+
148+
<footer>
149+
Built with <span className="kbd">React 18</span> and Flask on Elide.
150+
</footer>
151+
</body>
152+
</html>
153+
);
154+
}
155+
156+
export default async function render(props): Promise<string> {
157+
return renderToString(
158+
<App
159+
nowISO={new Date().toISOString()}
160+
ua={props.user_agent ?? ""}
161+
reqId={Date.now()}
162+
pid={process.pid}
163+
/>,
164+
);
165+
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
:root {
2+
--bg-start: #0b0c10;
3+
--bg-end: #111827;
4+
--fg: #e5e7eb;
5+
--muted: #9ca3af;
6+
--accent: #60a5fa;
7+
}
8+
* {
9+
box-sizing: border-box;
10+
}
11+
body {
12+
margin: 0;
13+
font-family:
14+
"Roboto",
15+
-apple-system,
16+
BlinkMacSystemFont,
17+
"Segoe UI",
18+
sans-serif;
19+
background: linear-gradient(180deg, var(--bg-start), var(--bg-end));
20+
color: var(--fg);
21+
}
22+
header {
23+
padding: 48px 24px;
24+
text-align: center;
25+
}
26+
h1 {
27+
font-size: clamp(28px, 3vw, 48px);
28+
margin: 0 0 16px;
29+
letter-spacing: -0.02em;
30+
font-family: "Roboto Condensed", "Roboto", "Segoe UI", sans-serif;
31+
}
32+
p.lead,
33+
.tagline {
34+
margin: 0;
35+
color: var(--muted);
36+
font-size: 18px;
37+
}
38+
.tagline {
39+
max-width: 720px;
40+
margin: 8px auto 0;
41+
text-align: center;
42+
}
43+
main {
44+
max-width: 880px;
45+
margin: 48px auto 64px;
46+
padding: 0 24px;
47+
}
48+
.card {
49+
background: #111318;
50+
border: 1px solid #1f2430;
51+
border-radius: 16px;
52+
padding: 20px;
53+
}
54+
.grid {
55+
display: grid;
56+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
57+
gap: 24px;
58+
}
59+
.code-sections {
60+
margin-top: 48px;
61+
display: grid;
62+
grid-template-columns: 1fr;
63+
gap: 24px;
64+
}
65+
.code-card {
66+
background: #0f172a;
67+
border: 1px solid #1f2430;
68+
border-radius: 16px;
69+
padding: 20px;
70+
}
71+
.code-card h3 {
72+
margin: 0 0 12px;
73+
font-size: 18px;
74+
}
75+
.snippet-subtitle {
76+
margin: -8px 0 16px;
77+
color: var(--muted);
78+
font-size: 14px;
79+
}
80+
.code-card pre {
81+
margin: 0;
82+
padding: 16px;
83+
border-radius: 12px;
84+
background: #05070f;
85+
border: 1px solid #1f2937;
86+
color: #e2e8f0;
87+
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
88+
monospace;
89+
font-size: 14px;
90+
overflow-x: auto;
91+
}
92+
.code-card code {
93+
display: block;
94+
white-space: pre;
95+
}
96+
.code-card .hljs-comment,
97+
.code-card .hljs-quote {
98+
color: #64748b;
99+
font-style: italic;
100+
}
101+
.code-card .hljs-keyword,
102+
.code-card .hljs-attr,
103+
.code-card .hljs-meta {
104+
color: #c084fc;
105+
font-weight: 600;
106+
}
107+
.code-card .hljs-string,
108+
.code-card .hljs-title,
109+
.code-card .hljs-name {
110+
color: #facc15;
111+
}
112+
.code-card .hljs-built_in,
113+
.code-card .hljs-function,
114+
.code-card .hljs-title.function_ {
115+
color: #38bdf8;
116+
}
117+
.code-card .hljs-number,
118+
.code-card .hljs-literal,
119+
.code-card .hljs-symbol {
120+
color: #f97316;
121+
}
122+
.code-card .hljs-attribute,
123+
.code-card .hljs-property {
124+
color: #f472b6;
125+
}
126+
.code-card .hljs-operator,
127+
.code-card .hljs-punctuation {
128+
color: #94a3b8;
129+
}
130+
.code-card .hljs-params {
131+
color: #93c5fd;
132+
}
133+
.tag {
134+
display: inline-block;
135+
padding: 4px 10px;
136+
border-radius: 999px;
137+
background: #0f172a;
138+
border: 1px solid #1f2937;
139+
color: #cbd5e1;
140+
font-size: 12px;
141+
}
142+
a.hero {
143+
display: inline-flex;
144+
align-items: center;
145+
gap: 10px;
146+
padding: 10px 14px;
147+
margin-bottom: 24px;
148+
border-radius: 12px;
149+
border: 1px solid #1f2937;
150+
background: #0f172a;
151+
color: #dbeafe;
152+
text-decoration: none;
153+
}
154+
footer {
155+
text-align: center;
156+
color: var(--muted);
157+
padding: 32px 0 64px;
158+
}
159+
.kbd {
160+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
161+
font-size: 12px;
162+
background: #0b1220;
163+
border: 1px solid #172136;
164+
padding: 2px 6px;
165+
border-radius: 6px;
166+
}
167+
img.logo {
168+
width: 40px;
169+
height: 40px;
170+
display: block;
171+
}

0 commit comments

Comments
 (0)