Cloudflare, Svelte and TailwindCSS 2024 and Beyond

A starter template for direct integration with Cloudflare Pages, its associated tools such as wrangler with both Sveltekit and TailwindCSS.
web
css
cloudflare
tailwindcss
svelte
javascript
Author
Affiliation

miah0x41

Published

December 24, 2024

Modified

December 24, 2024

Three logos of Cloudflare Pages, Svelte and TailwindCSS

The purpose of this guide is to show how to use the create-cloudflare Command Line Interface (CLI) or C3 to create a new project directory, with Cloudflare dependencies but configured for SvelteKit and TailwindCSS. This guide replaces the previously posted: Cloudflare, Svelte and TailwindCSS Template and Starter Kit, which is now deprecated.

The only command required is:

# Create cloudflare project
bun create cloudflare@latest c3-svelte-template --framework=svelte

The commands use the popular bun runtime, but equally you could use the pnpm tool or the default npm command.`

The guide is based on the official Cloudflare SvelteKit guide. The repository created using this guide is also available on GitHub as an alternative to running the command above:

c3-svelte-tailwind-v2

Node Runtime Setup

It’s recommended to use the Node Version Manager tool to control and manage different versions of node.js. Installation is relatively simple, using the supplied script.

The following commands list the available versions of node.js and what is currently installed:

# List versions available for download
<snip>
->      v20.5.0
<snip>
       v20.18.0   (LTS: Iron)
       v20.18.1   (Latest LTS: Iron)
        v21.0.0
        v21.1.0
       v22.10.0
<snip>
       v22.11.0   (LTS: Jod)
       v22.12.0   (Latest LTS: Jod)
        v23.0.0
<snip>

# Installed versions
nvm ls
->      v20.5.0
         system
default -> stable (-> v20.5.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v20.5.0) (default)
stable -> 20.5 (-> v20.5.0) (default)
lts/* -> lts/jod (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12 (-> N/A)
lts/fermium -> v14.21.3 (-> N/A)
lts/gallium -> v16.20.2 (-> N/A)
lts/hydrogen -> v18.20.5 (-> N/A)
lts/iron -> v20.18.1 (-> N/A)
lts/jod -> v22.12.0 (-> N/A)

We can ascertain the current system version with the run command:

# Current system version
nvm run system --version

Running node system (npm v8.19.3)
v18.13.0

There are two different versions of node.js installed the system version and v20.5.0, which is now out of date. We can install common versions using various aliases such as stable or lts.

# Install latest LTS
nvm install --lts

Installing latest LTS version.
Downloading and installing node v22.12.0...
Downloading https://nodejs.org/dist/v22.12.0/node-v22.12.0-linux-x64.tar.xz...
################################################################## 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v22.12.0 (npm v10.9.0)

# Check versions installed
nvm ls

        v20.5.0
->     v22.12.0
         system
default -> stable (-> v22.12.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v22.12.0) (default)
stable -> 22.12 (-> v22.12.0) (default)
lts/* -> lts/jod (-> v22.12.0)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12 (-> N/A)
lts/fermium -> v14.21.3 (-> N/A)
lts/gallium -> v16.20.2 (-> N/A)
lts/hydrogen -> v18.20.5 (-> N/A)
lts/iron -> v20.18.1 (-> N/A)
lts/jod -> v22.12.0

In the example above, nvm has automatically switched to the newly installed version. In most instances, typically switching to the Long Term Support (LTS) version is done with nvm use lts. However, some systems can struggle to recognise nvm on its first use hence switching to the system version first resolves most of these issues:

# Switch to LTS (or stable)
nvm use system && nvm use stable

Now using system version of node: v18.13.0 (npm v8.19.3)
Now using node v22.12.0 (npm v10.9.0)

Cloudflare Setup

The following command launches the Cloudflare wizard but pre-populates the framework as svelte due to the -- framework=svelte flag. The next set of questions are from the new Svelte CLI tool called sv, which asks for the type of Svelte project (SvelteKit minimal), typescript support before selecting addons such as prettier and tailwindcss.

This is followed by configuration of the selected addons, in this instance tailwindcss and its plugins of typography and forms. Finally, the selection of the package manager as (bun).

cloudflate-create
# Launch wizard
bun create cloudflare@latest c3-svelte-template --framework=svelte

─────────────────────────────────────────────────────────────────────────
👋 Welcome to create-cloudflare v2.35.1!
🧡 Let's get started.
📊 Cloudflare collects telemetry about your usage of Create-Cloudflare.

Learn more at: https://github.com/cloudflare/workers-sdk/blob/main/packages/create-cloudflare/telemetry.md
─────────────────────────────────────────────────────────────────────────

╭ Create an application with Cloudflare Step 1 of 3

├ In which directory do you want to create your application?
│ dir ./c3-svelte-template

├ What would you like to start with?
│ category Framework Starter

├ Which development framework do you want to use?
│ framework SvelteKit

├ Continue with SvelteKit via `bunx [email protected] create c3-svelte-template`


┌  Welcome to the Svelte CLI! (v0.6.7)

◇  Which template would you like?
│  SvelteKit minimal

◇  Add type checking with Typescript?
│  Yes, using Typescript syntax

◆  Project created

◇  What would you like to add to your project? (use arrow keys / space
bar)
│  prettier, tailwindcss

◇  tailwindcss: Which plugins would you like to add?
│  typography, forms

◇  Which package manager do you want to install dependencies with?
│  bun

◆  Successfully setup add-ons

◆  Successfully installed dependencies

◇  Successfully formatted modified files

◇  Project next steps ─────────────────────────────────────────────────────╮


│  1: cd c3-svelte-template

│  2: git init && git add -A && git commit -m "Initial commit" (optional)  │
│  3: bun dev --open



│  To close the dev server, hit Ctrl-C



│  Stuck? Visit us at https://svelte.dev/chat



├──────────────────────────────────────────────────────────────────────────╯

└  You're all set!


 Copying template files
 files copied to project directory

 Application created

 Configuring your application for Cloudflare Step 2 of 3

 Installing wrangler A command line tool for building Cloudflare Workers
 installed via `bun install wrangler --save-dev`

 Installing @cloudflare/workers-types
 installed via bun

 Adding latest types to `tsconfig.json`
 added @cloudflare/workers-types/2023-07-01

 Retrieving current workerd compatibility date
 compatibility date 2024-12-18

 Adding the Cloudflare Pages adapter
 installed @sveltejs/adapter-cloudflare

├ Changing adapter in svelte.config.js

├ Updating global type definitions in app.d.ts

 Adding Wrangler files to the .gitignore file
 updated .gitignore file

 Updating `package.json` scripts
 updated `package.json`

 Do you want to use git for version control?
 yes git

 Initializing git repo
 initialized git

 Committing new files
 git commit

 Application configured

 Deploy with Cloudflare Step 3 of 3

 Do you want to deploy your application?
 no deploy via `bun run deploy`

 Done

────────────────────────────────────────────────────────────
🎉  SUCCESS  Application created successfully!

💻 Continue Developing
Change directories: cd c3-svelte-template
Start dev server: bun run dev
Deploy: bun run deploy

📖 Explore Documentation
https://developers.cloudflare.com/pages

🐛 Report an Issue
https://github.com/cloudflare/workers-sdk/issues/new/choose

💬 Join our Community
https://discord.cloudflare.com
────────────────────────────────────────────────────────────

The tailend of the wizard asks whether we want to deploy using wrangler and I’ve selected no as we’ll illustrate that later.

Validation & Post-Install Config

The single command provided a working configuration for all three elements: the ability to deploy on Cloudflare, use the SvelteKit framework and utilise TailwindCSS.

Navigate to the newly created folder:

# Navigate to project folder
cd c3-svelte-template

The code below illustrates how the C3 tool has updated the svelte.config.js file:

svelte.config.js
1import adapter from "@sveltejs/adapter-cloudflare";
2import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    preprocess: vitePreprocess(),

    kit: {
        adapter: adapter()
    }
};

export default config;
1
The default adapter-auto has been replaced with adapter-cloudflare.
2
vitePreprocess has been added for TailwindCSS support.
tailwind.config.ts
import forms from '@tailwindcss/forms'; // <2>
import typography from '@tailwindcss/typography';
import type { Config } from 'tailwindcss';

export default {
  content: ['./src/**/*.{html,js,svelte,ts}'], // <1>

  theme: {
          extend: {}
  },

  plugins: [typography, forms] // <3>
} satisfies Config;
  1. Note the addition of svelte files.
  2. TailwindCSS plugins have been imported.
  3. TailwindCSS plugins have been registered.

The Cascading Style Sheets (CSS) under the src folder has also been updated:

src/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;

This is directly imported into the default route at +layout.svelte:

+layout.svelte
<script lang="ts">
  import '../app.css';
  let { children } = $props();
</script>

{@render children()}

Git Repository

The c3 utility creates a project folder, installs the selected packages, initiates a git repository and makes the first commit. To connect this to an external repository, follow the Cloudflare Github Rep Instructions.

Create the external repository; for GitHub (if logged in) use this shortcut URL: repo.new.

Once created navigate to the project folder (i.e. cd into it) and enter the following commands:

# Add remote origin
git remote add origin [email protected]:miah0x41/c3-svelte-tailwind-v2.git

# Rename current branch to main
git branch -M main

# Track the main branch upstream
git branch --set-upstream-to=origin/main main

Branch 'main' set up to track remote branch 'main' from 'origin'.

# Set upstream branch
git push --set-upstream origin margin

Attempting to either pull or push will lead to an error. If the upstream (external) git repository was empty then the commands should be sufficient. If however, if additional files were created such as LICENSE or README.md, then there is a mismatch between the local and remote git status.

To resolve, we need to pull the external repo first and resolve any conflicts:

# Attempt merges (default)
git config pull.rebase false

# Attempt pull
git pull

fatal: refusing to merge unrelated histories

# Fix with correct flag to override histories
git pull origin main --allow-unrelated-histories

From github.com:miah0x41/c3-svelte-tailwind-v2
 * branch            main       -> FETCH_HEAD
Merge made by the 'ort' strategy.
 LICENSE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)
 create mode 100644 LICENSE

# Push changes to remote repo
git push

Enumerating objects: 32, done.
Counting objects: 100% (32/32), done.
Delta compression using up to 20 threads
Compressing objects: 100% (28/28), done.
Writing objects: 100% (31/31), 47.63 KiB | 1.91 MiB/s, done.
Total 31 (delta 3), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (3/3), done.
To github.com:miah0x41/c3-svelte-tailwind-v2.git
   da5bf60..9f18021  main -> main

Deployment

To test the configuration, run the development server:

# Run dev server
bun run dev -- --open

$ vite dev --open
Forced re-optimization of dependencies

  VITE v6.0.5  ready in 2632 ms

    Local:   http://localhost:5173/
    Network: use --host to expose
    press h + enter to show help

To test the configuration, update the +page.svelte with some TailwindCSS classes:

src/routes/+page.svelte
<div class="flex h-screen items-center justify-center bg-gray-100">
    <div class="flex items-center rounded-lg bg-white p-4 shadow-lg">
        <img src="logo_youtube.png" alt="Logo" class="mr-4 h-20" />
        <span class="text-4xl font-semibold">Curious Data Explorer</span>
    </div>
</div>

The output should reflect the TailwindCSS classes:

Screenshot of TailwindCSS classes on a development page

The project is now ready for deployment but first we need to check what the site looks and behaves like in the Cloudflare infrastructure using the preview command:

bun-run-preview.sh
bun run preview

$ bun run build && wrangler pages dev
$ vite build
vite v6.0.5 building SSR bundle for production...
 153 modules transformed.
vite v6.0.5 building for production...
 131 modules transformed.
.svelte-kit/output/client/_app/version.json
      0.03 kB │ gzip:  0.05 kB
.svelte-kit/output/client/.vite/manifest.json
      3.08 kB │ gzip:  0.58 kB
.svelte-kit/output/client/_app/immutable/assets/0.VTS2zFfF.css
     10.98 kB │ gzip:  2.79 kB
.svelte-kit/output/client/_app/immutable/chunks/legacy.CHFpNJ_D.js
      0.04 kB │ gzip:  0.06 kB
.svelte-kit/output/client/_app/immutable/entry/start.D4CGAcgR.js
      0.07 kB │ gzip:  0.08 kB
.svelte-kit/output/client/_app/immutable/chunks/index-client.DNI3gCIi.js
      0.33 kB │ gzip:  0.26 kB
.svelte-kit/output/client/_app/immutable/nodes/0.vec3NjHj.js
      0.38 kB │ gzip:  0.27 kB
.svelte-kit/output/client/_app/immutable/nodes/2.BCXTYHEi.js
      0.44 kB │ gzip:  0.32 kB
.svelte-kit/output/client/_app/immutable/chunks/disclose-version.D48o4b0F.js   0.98 kB │ gzip:  0.56 kB
.svelte-kit/output/client/_app/immutable/nodes/1.Bg7X2pRM.js
      1.26 kB │ gzip:  0.69 kB
.svelte-kit/output/client/_app/immutable/chunks/render.Bk9H9iQr.js
      2.48 kB │ gzip:  1.37 kB
.svelte-kit/output/client/_app/immutable/entry/app.CJSKkdwc.js
      9.61 kB │ gzip:  4.39 kB
.svelte-kit/output/client/_app/immutable/chunks/runtime.DVmAVPAN.js
     12.44 kB │ gzip:  5.02 kB
.svelte-kit/output/client/_app/immutable/chunks/entry.CA7GdDSB.js
     31.32 kB │ gzip: 12.24 kB
 built in 1.52s
.svelte-kit/output/server/.vite/manifest.json                          1.77 kB
.svelte-kit/output/server/_app/immutable/assets/_layout.VTS2zFfF.css  10.98 kB
.svelte-kit/output/server/entries/pages/_layout.svelte.js              0.24 kB
.svelte-kit/output/server/internal.js                                  0.31 kB
.svelte-kit/output/server/entries/pages/_page.svelte.js                0.35 kB
.svelte-kit/output/server/chunks/equality.js                           0.61 kB
.svelte-kit/output/server/chunks/index.js                              2.29 kB
.svelte-kit/output/server/entries/fallbacks/error.svelte.js            3.08 kB
.svelte-kit/output/server/chunks/exports.js                            7.53 kB
.svelte-kit/output/server/chunks/internal.js                          42.11 kB
.svelte-kit/output/server/index.js                                    95.88 kB
 built in 9.71s

Run npm run preview to preview your production build locally.

> Using @sveltejs/adapter-cloudflare
   done

 ⛅️ wrangler 3.99.0
-------------------

 Compiled Worker successfully
 Parsed 2 valid header rules.
[wrangler:inf] Ready on http://localhost:8788
 Starting local server...
[wrangler:inf] GET / 200 OK (95ms)
[wrangler:inf] GET /_app/immutable/assets/0.VTS2zFfF.css 200 OK (63ms)
[wrangler:inf] GET /_app/immutable/chunks/disclose-version.D48o4b0F.js 200 OK (29ms)
[wrangler:inf] GET /_app/immutable/entry/start.D4CGAcgR.js 200 OK (97ms)
[wrangler:inf] GET /_app/immutable/chunks/index-client.DNI3gCIi.js 200 OK (198ms)
[wrangler:inf] GET /_app/immutable/chunks/legacy.CHFpNJ_D.js 200 OK (98ms)
[wrangler:inf] GET /_app/immutable/entry/app.CJSKkdwc.js 200 OK (184ms)
[wrangler:inf] GET /_app/immutable/chunks/render.Bk9H9iQr.js 200 OK (180ms)
[wrangler:inf] GET /logo_youtube.png 200 OK (68ms)
[wrangler:inf] GET /_app/immutable/nodes/0.vec3NjHj.js 200 OK (23ms)
[wrangler:inf] GET /_app/immutable/chunks/runtime.DVmAVPAN.js 200 OK (136ms)
[wrangler:inf] GET /_app/immutable/chunks/entry.CA7GdDSB.js 200 OK (116ms)
[wrangler:inf] GET /_app/immutable/nodes/2.BCXTYHEi.js 200 OK (97ms)
[wrangler:inf] GET /favicon.png 200 OK (25ms)
[wrangler:inf] GET /_app/immutable/nodes/1.Bg7X2pRM.js 200 OK (17ms)
[wrangler:inf] GET /favicon.png 200 OK (19ms)

We can now deploy to Cloudflare, however note the prompts at the bottom. The first is to get an authentication key for wrangler via the browser.

bun-run-deploy-1.sh
# Attempt deployment
bun run deploy

$ bun run build && wrangler pages deploy
$ vite build
vite v6.0.5 building SSR bundle for production...
 153 modules transformed.
vite v6.0.5 building for production...
 131 modules transformed.
.svelte-kit/output/client/_app/version.json
      0.03 kB │ gzip:  0.05 kB
.svelte-kit/output/client/.vite/manifest.json
      3.08 kB │ gzip:  0.58 kB
.svelte-kit/output/client/_app/immutable/assets/0.VTS2zFfF.css
     10.98 kB │ gzip:  2.79 kB
.svelte-kit/output/client/_app/immutable/chunks/legacy.CHFpNJ_D.js
      0.04 kB │ gzip:  0.06 kB
.svelte-kit/output/client/_app/immutable/entry/start.BmoqV0fV.js
      0.07 kB │ gzip:  0.08 kB
.svelte-kit/output/client/_app/immutable/chunks/index-client.DNI3gCIi.js
      0.33 kB │ gzip:  0.26 kB
.svelte-kit/output/client/_app/immutable/nodes/0.vec3NjHj.js
      0.38 kB │ gzip:  0.27 kB
.svelte-kit/output/client/_app/immutable/nodes/2.BCXTYHEi.js
      0.44 kB │ gzip:  0.32 kB
.svelte-kit/output/client/_app/immutable/chunks/disclose-version.D48o4b0F.js   0.98 kB │ gzip:  0.56 kB
.svelte-kit/output/client/_app/immutable/nodes/1.CzBC74I5.js
      1.26 kB │ gzip:  0.69 kB
.svelte-kit/output/client/_app/immutable/chunks/render.Bk9H9iQr.js
      2.48 kB │ gzip:  1.37 kB
.svelte-kit/output/client/_app/immutable/entry/app.BR5rdV8r.js
      9.61 kB │ gzip:  4.39 kB
.svelte-kit/output/client/_app/immutable/chunks/runtime.DVmAVPAN.js
     12.44 kB │ gzip:  5.02 kB
.svelte-kit/output/client/_app/immutable/chunks/entry.BeExm4ad.js
     31.32 kB │ gzip: 12.24 kB
 built in 858ms
.svelte-kit/output/server/.vite/manifest.json                          1.77 kB
.svelte-kit/output/server/_app/immutable/assets/_layout.VTS2zFfF.css  10.98 kB
.svelte-kit/output/server/entries/pages/_layout.svelte.js              0.24 kB
.svelte-kit/output/server/internal.js                                  0.31 kB
.svelte-kit/output/server/entries/pages/_page.svelte.js                0.35 kB
.svelte-kit/output/server/chunks/equality.js                           0.61 kB
.svelte-kit/output/server/chunks/index.js                              2.29 kB
.svelte-kit/output/server/entries/fallbacks/error.svelte.js            3.08 kB
.svelte-kit/output/server/chunks/exports.js                            7.53 kB
.svelte-kit/output/server/chunks/internal.js                          42.11 kB
.svelte-kit/output/server/index.js                                    95.88 kB
 built in 7.94s

Run npm run preview to preview your production build locally.

> Using @sveltejs/adapter-cloudflare
   done
Attempting to login via OAuth...
Opening a link in your default browser: https://dash.cloudflare.com/oauth2/auth?response_type=code&client_id=54d11594-84e4-41aa-b438-e81b8fa78ee7&redirect_uri=http%3A%2F%2Flocalhost%3A8976%2Foauth%2Fcallback&scope=account%3Aread%20user%3Aread%20workers%3Awrite%20workers_kv%3Awrite%20workers_routes%3Awrite%20workers_scripts%3Awrite%20workers_tail%3Aread%20d1%3Awrite%20pages%3Awrite%20zone%3Aread%20ssl_certs%3Awrite%20ai%3Awrite%20queues%3Awrite%20pipelines%3Awrite%20offline_access&state=4oAPIYvDHoIOCH3ZFiIvMs1O_E2UVBYu&code_challenge=FzlKQQnIXMz7nEIVtWgAdWdxpyiQfRuife1ZPkT8Xg4&code_challenge_method=S256

Once logged in, the prompt for the authorisation of wrangler is presented:

Wrangle Authorisation

Post authorisation, you can close the browser window down:

Post Authorisation Screen

The tool has recognised that no Cloudflare project exists and creates one and confirms the branch prior to deployment.

bun-run-deploy-2.sh
Successfully logged in.
 The project you specified does not exist: "c3-svelte-template". Would you like to create it? › Create a new project
 Enter the production branch name: … main
 Successfully created the 'c3-svelte-template' project.
 [WARNING] Warning: Your working directory is a git repo and has uncommitted changes

  To silence this warning, pass in --commit-dirty=true


 Success! Uploaded 19 files (2.80 sec)

 Uploading _headers
 Compiled Worker successfully
 Uploading Worker bundle
 Uploading _routes.json
🌎 Deploying...
 Deployment complete! Take a peek over at https://2e6b0ffb.c3-svelte-template.pages.dev
Dirty Repository

Whilst the wrangler tool does a significant amount of compilation, the source is based on the committed changes, so any not committed is excluded. So if the URL provided is blank or it appears unusual, check of any uncommitted changes.

That concludes this guide, which configures a Cloudflare project with SvelteKit and TailwindCSS using a single command.

Attribution

Images based on:

Back to top

Citation

BibTeX citation:
@online{2024,
  author = {, miah0x41},
  title = {Cloudflare, {Svelte} and {TailwindCSS} 2024 and {Beyond}},
  date = {2024-12-24},
  url = {https://blog.curiodata.pro/posts/10-svelte-cloudflare-update/},
  langid = {en}
}
For attribution, please cite this work as:
miah0x41., “Cloudflare, Svelte and TailwindCSS 2024 and Beyond,” Dec. 24, 2024. Available: https://blog.curiodata.pro/posts/10-svelte-cloudflare-update/