node.jscontent-management-systemsveltesveltekit

How can I programmatically generate SvelteKit routes?


I've been working on a simple CMS with SvelteKit. I want authenticated users to be able to create new pages or layouts within the application. I'm not sure if this would require the actual generation of +page.svelte or +layout.svelte files programmatically, or some way of dynamically loading routes from data (almost as if there was an equivalent of loading a list of components with an #each block, but for whole pages with their own url routes). Right now I'm storing data in JSON, along with the content for each page. It also seems a bit silly to create a bunch of empty directories simply for routing and layout inheritance purposes when all my data is already stored in JSON.

Would I do this using something like Node's File System API, or is there a SvelteKit specific way to generate directories, or load file routes dynamically from data?


Solution

  • After you have built your app, you can't dynamically add more +page.svelte or +layout.svelte files to it. So your users can only create data when they use your app. So somewhere on the server, you need to store data representing these new pages (e.g. an array with objects, where each object contains the data for respective page), and then you have one +page.svelte file to display one of those objects. To indicate which one, use a slug parameter.

    A similar approach can also be used for layouts.


    It can look something like this:

    <script>
        
        const pageContent = `
            <h1>Hello!</h1>
            <p>How are you?</p>
        `
        
        // Either use this...
        const premadeLayoutOptions = {
            name: `plain`,
            headerName: `Header Name`,
            // ...
        }
        
        // Or use this...
        const customizableLayoutOptions = {
            headerContent: `<h1>Header Content</h1>`,
            menuContent: `<h1>Menu Content</h1>`,
            footerContent: `<h1>Footer Content</h1>`,
        }
        
        // Or use this...
        const customLayout = {
            code: `
                <div class="layout">
                    
                    <header>
                        Hello!
                    </header>
                    
                    <main>
                        {{pageContent}}
                    </main>
                    
                </div>
            `
        }
        
        
    </script>
    
    {#if premadeLayoutOptions}
        
        {#if premadeLayoutOptions.name == `none`}
            <div class="layout none">
                <div class="content">
                    {@html pageContent}
                </div>
            </div>
        {:else if premadeLayoutOptions.name == `plain`}
            <div class="layout plain">
                <header>
                    {premadeLayoutOptions.headerName}
                </header>
                <div class="content">
                    {@html pageContent}
                </div>
            </div>
        {/if}
        <!-- And then you have more premade layouts like this... -->
        
    {/if}
    
    {#if customizableLayoutOptions}
        <div class="layout customizable">
            
            <header>
                {@html customizableLayoutOptions.headerContent}
            </header>
            
            <header>
                {@html customizableLayoutOptions.menuContent}
            </header>
            
            <div class="content">
                {@html pageContent}
            </div>
            
            <header>
                {@html customizableLayoutOptions.footerContent}
            </header>
            
        </div>
        
    {/if}
    
    {#if customLayout}
        
        {@html customLayout.code.replace(`{{pageContent}}`, pageContent)}
        
    {/if}