flask-svelte-template

Flask Svelte Template

Template with functionality for integrating Svelte UI Framework on Vite development server into a Flask app

Using the Svelte UI Framework within a Python Flask Application

The goal here is to develop Svelte components/views using the Vite dev server. Then, when it's time to connect them to the actual backend (i.e. Flask), building and adapting to the structure of a Flask app, transpiling into regular Javascript and moving the bundled files into the Flask app around it (in the file tree) and updating the file paths in the code accordingly. This will make it as simple as hooking up the .html file like any other:

@app.route('/about')
def about():
  return render_template('intro/about.html')

Here's an example of the file structure for the Flask app, and then for the Svelte development:

my-flask-app/
├─ static/
│  ├─ intro/
│  │  ├─ css/
│  │  ├─ img/
│  ├─ auth/
│  │  ├─ css/
│  │  ├─ js/
|  ├─ shared/
├─ templates/
│  ├─ intro/
│  ├─ auth/
│  ├─ base.html
├─ __init__.py
├─ db.py
├─ intro.py
├─ auth.py
svelte/
├─ src/
│  ├─ intro/
│  │  ├─ about/
│  │  │  ├─ assets/
│  │  │  ├─ App.svelte
│  │  │  ├─ index.html
│  │  │  ├─ main.js
│  ├─ auth/
│  ├─ shared/
├─ vite.config.js
├─ package.json
├─ build-svelte.py
instance/
.gitignore
unibeautifyrc.yaml

To better sort concerns, we have general "sections" of the website, containing related pages. In the above example that would be "intro" and "auth", with "intro" having the "about" page.

Rules

In svelte/src/ the directories must be the section names, which must be the exact same names as the directories in my-flask-app/templates/ which must be the same as in my-flask-app/static/.
In svelte/src/<section-name>/ the directories must be the desired names of the .html files when these get built later into "templates". Within those directories can be generic filenames like index.html, main.js, App.svelte, etc. and within assets/ you can sort and nest your files however you want (these things will get replaced when being built). Note that the Vite development server only recognizes specifically index.html as an entry point, and then for the about page it'd be http://localhost:3000/intro/about/ (the trailing slash is important).
In svelte/ all assets folders must always be called assets.
In svelte/vite.config.js, the build.rollupOptions.input object needs to contain key/value pairs for all the pages you want to build, where the key is the desired name of the .html file (exactly like the names of the directories within svelte/src/<section-name>/), and the value is the path to the index.html file. Like this:

import { defineConfig } from "vite";
import { resolve, dirname } from "path";

// ...boilerplate...

const entryPoints = {
  // HERE
  about: resolve(root, "intro/about/index.html"),
};

// ...
export default defineConfig({
  build: {
    rollupOptions: {
      input: entryPoints,
    },
  },
});

Using it

The idea is to have the development of a regular Flask app, and then when you want to develop views with a more advanced frontend, switch to the svelte folder, where you use the Vite development server to develop the Svelte components. As the package.json defines, simply use npm run dev to start.
Of course during this development, the frontend will still need to communicate with a backend. This would typically be done with {{ url_for() }} to make fetch requests along with other Jinja templates, however we obviously don't have Jinja here, and the backend wouldn't be running (or at most on a different domain). There isn't much that can be done here, and you would just kind of have to pretend that such code will work and do development using test/placeholder values. Something like this, for example:

<head>
  <!-- ... -->
</head>
<body>
  <!-- ... -->
  <script>
    let getUser, getNotifs, posts;
    try {
      if (!Number.parseInt("{{ 1 }}")) throw new Error("Jinja wasn't parsed.");

      getUser = "{{ url_for('auth.get_active') }}";
      getNotifs = "{{ url_for('blog.get_notifs', user=current_user.id) }}";
      posts = "{{ posts }}";
    } catch (e) {
      return;
    }
  </script>
  <script type="module" src="./main.js"></script>
</body>

Then in your App.svelte you could assign test values at the top like this:

posts ??= [
  { title: "The Philosophy of Grasshoppers", body: "..." },
  { title: "An Adventure in Stubbornness and Ignorance", body: "..." },
];

// and then fetches later on as they show up:
const res = await (getNotifs ? fetch(getNotifs) : timeout(3, ["blah blah..."]));

function timeout(s, data) {
  return new Promise(function (resolve, _) {
    setTimeout(function () {
      resolve(data);
    }, s * 1000);
  });
}

This code can work in the Vite dev server and the Flask server without needing to change anything. Of course that doesn't forgive the fact that this is quite tedious, and the big pain point of this workflow, but it's the best solution I could come up with.

Anyways, once you'd like to move your new views into the Flask server, simply run npm run build. This will build the Svelte into regular Javascript and do all the annoying stuff like deleting old files, moving the bundled files into the flask app, updating all the links to static files (a.k.a. "assets" in Vite land) within the code to work on a flask app, and sorting it as intended. Once you've done this you can just set up a simple view (like at the top of this README) and it should work.

Note to run the Flask server get out of the "svelte" directory, set some environment variables set FLASK_APP=flask-app (this one's also required for the build command), set FLASK_ENV=development, and then flask run.

Running it (CMD)

git clone https://github.com/martinm07/flask-svelte-template.git
cd flask-svelte-template
py -3 -m venv venv
venv\Scripts\activate
pip install Flask
set FLASK_APP=flask-app
set FLASK_ENV=development
cd svelte
npm install
npm run build
cd ..
flask run

Top categories

svelte logo

Want a Svelte site built?

Hire a Svelte developer
Loading Svelte Themes