skip to Main Content

I’m creating a resume builder where many templates will be possible. I have a component that switches between the different templates depending on what the user chose. For now I only have three templates, but the file already compiles to 300Kb:

<script>
    import Student from '$lib/template/_student/Home.svelte';
    import Expert from '$lib/template/_expert/Home.svelte';
    import Artist from '$lib/template/_artist/Home.svelte';

    export let doc;
    $: templateId = doc?.templateId
    $: cssUrl = doc?.design?.color?.substring(1)
</script>

<div id='templateLoader'>
    {#if templateId == '1'}
        <Student {doc} on:modified/>
    {:else if templateId == '2'}
        <Expert {doc} on:modified/>
    {:else if templateId == '3'}
        <Artist {doc} on:modified/>
    {:else}
        <Expert {doc} on:modified/>
    {/if}
</div>

<svelte:head>
    <link rel="stylesheet" href={`/css/design.css?t=${templateId}&c=${cssUrl}`} />
</svelte:head>

I had an onMount() function that was loading the template dynamically, but that was a very unpleasant user experience (and it’s not good for SEO)

I was wondering if it is possible to tell SvelteKit to keep each template in a separate file, and to loda that file with an additional server request () which in SSR is unnoticable) after the {#if templateId} section is processed. In Next.js there is a dynamic import mechanism for that.

Any suggestions? I’m planning to have dozens of templates, so I cannot keep things this way.
Just to compare with other comps, this component alone is huge:

.svelte-kit/output/client/_app/immutable/chunks/ToolbarButton-5e81536b.js
11.11 kB │ gzip: 3.69 kB .svelte-kit/output/client/_app/immutable/components/pages/authorized/cv/ai/_page.svelte-d03f5080.js
14.39 kB │ gzip: 4.99 kB .svelte-kit/output/client/_app/immutable/chunks/TemplateDetector-88b690e9.js
305.29 kB │ gzip: 82.38 kB

Ideally, the solution would look like this:

<script>
    export let doc;
    $: templateId = doc?.templateId
    $: cssUrl = doc?.design?.color?.substring(1)

    function getTemplatePath(templateId) {
        switch( templateId ) {
            case '1' :
                return '$lib/template/_student/Home.svelte'
            case '2' :
                return '$lib/template/_expert/Home.svelte'
            case '3' :
                return '$lib/template/_artist/Home.svelte'
            default :
                return '$lib/template/_expert/Home.svelte'
        }
    }

    $: Template = null
        import(getTemplatePath(templateId) /* @vite-ignore */).then(loaded => Template = loaded)

</script>

<div id='templateLoader'>
    <Template {doc} on:modified/>

</div>

<svelte:head>
    <link rel="stylesheet" href={`/css/design.css?t=${templateId}&c=${cssUrl}`} />
</svelte:head>

But this throws a series of exceptions:

            const err = new Error(`Cannot find module '${id}' imported from '${importer}'`);
                        ^

Error: Cannot find module '$lib/template/_expert/Home.svelte' imported from 'C:/Users/zhamd/work/linkedinCv-back/src/lib/template/TemplateDetector.svelte'
    at nodeImport (file:///C:/Users/zhamd/work/linkedinCv-back/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-c167897e.js:54014:25)
    at ssrImport (file:///C:/Users/zhamd/work/linkedinCv-back/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-c167897e.js:53935:20)
    at ssrDynamicImport (file:///C:/Users/zhamd/work/linkedinCv-back/node_modules/.pnpm/[email protected]/node_modules/vite/dist/node/chunks/dep-c167897e.js:53962:16)
    at eval (/src/lib/template/TemplateDetector.svelte:26:2)
    at Object.$$render (/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.mjs:2042:22)
    at eval (/src/routes/cv/[id]/+page.svelte:49:147)
    at Object.$$render (/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.mjs:2042:22)
    at Object.default (/.svelte-kit/generated/root.svelte:102:133)
    at eval (/src/routes/+layout.svelte:26:33)
    at Object.$$render (/node_modules/.pnpm/[email protected]/node_modules/svelte/internal/index.mjs:2042:22) {
  code: 'ERR_MODULE_NOT_FOUND'
}

2

Answers


  1. Chosen as BEST ANSWER

    Just to followup after the answer of Tonton-Blax, this is how I ended up doing it:

    <script>
        const templates = {
            Student: () => import('$lib/template/_student/Home.svelte'),
            Expert: () => import('$lib/template/_expert/Home.svelte'),
            Artist: () => import('$lib/template/_artist/Home.svelte')
        };
    
        export let doc;
        $: templateId = doc?.templateId
        $: cssUrl = doc?.design?.color?.substring(1)
    
        async function loadTemplate() {
            // console.log('loading template ', templateId, ' resume: ', doc._id)
            return await templates[templateId]()
        }
    </script>
    
    <div id='templateLoader'>
        {#await loadTemplate() then template}
            <svelte:component this={template.default} {doc} on:modified/>
        {/await}
    </div>
    
    <svelte:head>
        <link rel="stylesheet" href={`/css/design.css?t=${templateId}&c=${cssUrl}`} />
    </svelte:head>
    

  2. Svelte components can be loaded dynamically with standard async import methods (await import (`./${yourComponent}.svelte`)

    Roughly based on your example you could do something of the like, but there’s plenty of way to achieve this.
    This article goes a bit more into detail if needed.

    <script>
        const templates = {
            student: () => import('./Student.svelte'),
            expert: () => import('./Expert.svelte'),
            artist: () => import('./Artist.svelte')
        };
        
        let component = 'student';
    </script>
    
    
    {#await templates[component]() then module}
        <svelte:component this={module.default}/>
    {/await}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search