Technical blog · published

Bringing My PHP Adapter to SvelteKit 3

Ryan Spice 3 min read

I updated sveltekit-php for SvelteKit 3, tested its PHP and Node-sidecar output, and mapped the work still needed across Canopy Digital's sites.

An illustrated bridge connects frontend code to PHP hosting, with a separate branch to a Node server.
Original AI illustration generated with Codex.
Contents7 sections
Back RSS

I built my sveltekit-php adapter to put SvelteKit sites on PHP hosting without pretending the runtime doesn't matter. With SvelteKit 3, my adapter needed real changes to stay useful.

As of October 6, 2026, the candidate is 1.1.0-alpha.0. It's committed locally and unpublished ÔÇö you won't get it from npm's latest tag, which still points to 1.0.1. That's intentional until hosted checks and release prep are done.

What changed in SvelteKit 3

SvelteKit 3 moves config into the Vite plugin and changes the adapter build contract. I kept Kit 2 working instead of forking.

I added a small compatibility facade so my existing internals keep their shape while accepting Kit 3's flat config. The server bootstrap now branches: Kit 3 uses generateServerInstance, Kit 2 keeps its manifest path. I also wired the sidecar's asset reader to a file stream and aligned instrumentation with the available hooks. This is actual runtime work, not just widening a peer dependency.

For a prerendered marketing build, the Kit 3 configuration now lives in vite.config.js. This example targets the local candidate; the current npm release does not yet include these changes.

import { sveltekit } from '@sveltejs/kit/vite';
import adapter from 'sveltekit-php';
 
export default {
  plugins: [sveltekit({
    adapter: adapter({
      mode: 'php-static',
      out: 'build',
      assets: 'build',
      strict: true
    }),
    paths: { base: '/sites/cos' },
    prerender: { entries: ['/'], crawl: false }
  })]
};

Use an empty base for root hosting and list the public routes your marketing build needs. Kit 2 keeps its existing svelte.config.js setup.

Runtime fixes, not just a version range

That work surfaced two bugs worth fixing now.

My JavaScript SSR build was generating PHP stubs for JavaScript endpoints. Those stubs could intercept a request that belonged on the Node sidecar and fail before it ever got there. I removed that shadow path.

The PHP proxy also wasn't receiving the base path, so a root deploy worked while a deploy under /sites/cos didn't. It now gets the base and strips it consistently, handling home documents and data requests correctly.

What I verified

I verified this with my 130 unit tests plus 8 packed build and runtime cases. Those cases install the real built package into temporary projects and cover Kit 2 and Kit 3, php-static and js-ssr modes, each at root and at a subdirectory. They exercise routing, assets, the proxy, cookies, and sidecar rendering.

One unrelated type-check blocker remains in the repo, so I can't claim every check is green. I left it out of this change on purpose.

Other SvelteKit and PHP approaches

This isn't the only way to combine the two. idleberg's sveltekit-adapter-html-like prerenders template-oriented output, including PHP, with tag injection and custom extensions. Its README warns about an older Kit prerelease, so I don't treat it as verified Kit 3 support.

flexlex's SvelteKitPhp documents +page.server.php and +layout.server.php loaders, alongside limitations around initial SSR and form actions. These are different deployment contracts; changing a file extension doesn't make JavaScript server code run in PHP.

Where Canopy adoption stands

Where does that leave adoption? It's incomplete. CoS already has Kit 3.0.1 installed, while Canopy's main site and the shared Ryan and Canopy blogs remain on Kit 2 pins and earlier adapter copies. Don't read my local package results as live deployments.

One package, two deployment modes

CoS makes the split concrete. Its marketing routes fit well as php-static on PHP hosting. The normal app has Node endpoints that read files and use native integrations ÔÇö those still need a persistent Node runtime.

The important split is visible here: PHP-static serves prerendered documents; js-ssr adds a persistent JavaScript runtime behind the proxy.

SvelteKit 2 or 3 builds through sveltekit-php into either PHP-static output or a PHP proxy with a persistent Node sidecar.
Deployment modes in sveltekit-php. Original diagram by Ryan Spice and Codex.

Open the full-size deployment diagram.

For a build that requires the JavaScript sidecar, the adapter selection changes:

adapter({ mode: 'js-ssr', out: 'build', assets: 'build' })

That host must run the generated Node sidecar as well as PHP. See the adapter runtime documentation for the full setup.

Marketing PHP mode and the normal app must remain separate build modes. The fixtures proved js-ssr in isolation; they did not accept the entire real CoS app with this adapter.

What remains before rollout

Next I need to resolve the type blocker, pick an immutable candidate, publish through the proper workflow, and run consumer-specific and hosted PHP validation before anything rolls out.

Published
Updated
Author
Ryan Spice

Sources

Sources

Primary documentation and source material used for the factual claims in this article.

Further reading

Further reading

Related notes and background material worth opening next.

Back RSS