I'm asking in general, but I'll give you an example use-case. On a particular page I'm building, I'm using the Helper
component from Flowbite-Svelte. I keep finding myself using <Helper color="red">...</Helper>
to make the color red. It'd be nice to build a higher order component and just use that, e.g. <ErrorHelper>...</ErrorHelper>
, where ErrorHelper
automatically passes the color
prop as red
to Helper
.
I've been looking on the web for an hour and can't find out how to make a higher order component. I tried myself and came up with this:
import { Helper } from 'flowbite-svelte';
class ErrorHelper extends Helper {
constructor(vals) {
const newVals = {...vals, props: {...vals.props, color: 'red'}}
super(newVals);
}
}
This works when I render the component using Storybook for Svelte, which I think uses Svelte WITHOUT using SvelteKit. But when I use the same code in my SvelteKit website, like this:
// src/routes/onboard/3/+page.svelte
<script lang="ts">
import OnboardStep3 from '$lib/components/OnboardStep3.svelte';
</script>
<OnboardStep3 />
I get this error:
TypeError: Class extends value #<Object> is not a constructor or null
at /src/lib/components/OnboardStep3.svelte:17:28
at Object.$$render (/node_modules/svelte/src/runtime/internal/ssr.js:156:16)
at eval (/src/routes/onboard/3/+page.svelte:15:100)
at Object.$$render (/node_modules/svelte/src/runtime/internal/ssr.js:156:16)
at Object.default (/.svelte-kit/generated/root.svelte:48:43)
at /node_modules/@sveltejs/kit/src/runtime/components/layout.svelte:5:41
at Object.$$render (/node_modules/svelte/src/runtime/internal/ssr.js:156:16)
at Object.default (/.svelte-kit/generated/root.svelte:47:42)
at eval (/src/routes/+layout.svelte:25:163)
at Object.$$render (/node_modules/svelte/src/runtime/internal/ssr.js:156:16)
Unless you disable SSR in SvelteKit, you will have to also create a valid SSR-component.
Unfortunately Svelte uses some internals rather than the public SSR API. I.e. you cannot just provide an object with a render
function but need to provide and use $$render
instead. (Both the client-side and the server-side APIs are significantly different in Svelte 5, so migration will be necessary either way.)
Here is an example:
import { browser } from '$app/environment';
import { Helper } from 'flowbite-svelte';
export const ErrorHelper = extend(Helper, { color: 'red' });
function extend(component, defaults) {
const extendProps = props => ({ ...defaults, ...props });
return browser
? class extends component {
constructor(options) {
const { props, ...rest } = options;
super({
...rest,
props: extendProps(props),
});
}
}
: {
$$render(result, props, bindings, slots, context) {
return component.$$render(
result,
extendProps(props),
bindings,
slots,
context
);
}
}
}