Store a custom site for every user ( dynamic content ) Svelte

68 Views Asked by At

I'm building an e-commerce marketplace with Firebase and Svelte where every user can create their store. Not every store will look the same. The users will structure their site using pre-made components and behind the scenes, it might look something like this:

<Header logo="" name"".../>
<FAQ questions=.../>
<ProductList .../>
<ContactForm .../>
<Footer .../>

or like:

<Header logo="" name"".../>
<Video link="".../>
<ProductList .../>
<MailingList .../>
<Footer .../>

And yes, I know about <svelte:component this={...}/> for dynamic components.

But how should I store the sites? Should I just keep the full ".svelte" file in a field of the store's document in the database, then on the client side just create every component with the technique I talked about above? This seems pretty inefficient and heavy in the long run. What's the standard way of doing it?

Thanks.

2

There are 2 best solutions below

5
brunnerh On BEST ANSWER

I would probably store the structure as JSON to decouple it from the implementation, e.g.

[
  { type: "header", name: "Maru Store" },
  { type: "video", link: "..." },
  { type: "product-list" },
  { type: "mailing-list" },
  { type: "footer" }
]

(Quite possibly with additional containers for layout purposes, e.g. vertical/horizontal/grid.)

Then a generic component can resolve this.

{#if descriptor.type == 'header'}
  <Header name={descriptor.name} />
{:else if ...}
  ...
{/if}

If e.g. a layout container is found, {#each} can be used with <svelte:self> for recursion.

This should not be that impactful for a few components. The DB query result can also be cached to prevent round trips.

1
Stephane Vanraes On

Building further on @H.B. answer, if you store the structures like this:

[
  { type: "header", name: "Maru Store" },
  { type: "video", link: "..." },
  { type: "product-list" },
  { type: "mailing-list" },
  { type: "footer" }
]

You can use a 'mapping' Object to link types and components:

<script>
  // other imports omitted for brevity
  import Header from './Header.svelte';
 
  // the blocks the user wants to have
  export const blocks = [];

  // the map
  const map = {
    'header': Header,
    'video': Video,
    'product-list': ProductList
  }

</script>

{#each blocks as block}
  <svelte:component this={block.type} {...block} />
{/each}

This has the benefit that all you have to do when adding new types is import them and change the map without going around with imports and as long as those different types of components are not overly large the extra overhead of bundling them all is negligible.

If you have hundreds of types of components you could try your hand at dynamically loading the components instead.