Build a markdown editor-viewer in Svelte

Before we dive into writing the code, this blog assumes that the reader knows the basics of Svelte(I will anyway explain each line of code).

What are we building?

  • A textarea to enter markdown data.
  • A button clicking on which will show formatted data.

Step 1:

Let's first create the textarea and the button and bind the textarea to the component.

<script>
    let rawData = '';
</script>

<main class='main-container'>
  <form class='form'>
    <textarea type='text' bind:value={rawData} class='markdown-input' placeholder='Enter your markdown content here...'/>
    <button class='show-btn' on:click={() => ({})}>
      Show Preview
    </button>
  </form>
</main>

Here, we have taken a main tag with class='main-container' and we will use it at the end for styling the UI. The <textarea type='text' bind:value={rawData} class='markdown-input' placeholder='Enter your markdown content here...'/> binds the textarea with rawData which means when we will change the value inside textarea, the same value will be updated in rawData variable. The button for now is not doing anything on click.

Step 2:

On clicking the button, we want to hide the editor and process the markdown value stored in rawData and show it in the UI. To process the markdown, we will use marked package which is quite easy to use. We need to conditionally show the rendered markdown only when the Show Preview button is clicked, and so we will use Svelte's if-else directive.

<script>
    import marked from 'marked';

    let rawData = '';
    let preview = false;

    function showPreview() {
        preview = true;
    }
</script>

<main class='main-container'>
    {#if preview}
        <div>
            {@html marked(rawData)}
        </div>
    {:else}
        <form class='form'>
            <textarea type='text' bind:value={rawData} class='markdown-input' placeholder='Enter your markdown content here...'/>
            <button class='show-btn' on:click={showPreview}>
             Show Preview
            </button>
    </form>
    {/if}
</main>

We have taken preview as false by default and based on it's value we are conditionally rendering either the form or the formatted markdown using @html marked(rawData).
If we only put this marked(rawData), it would render the html equivalent of the markdown as plain strings. We want the output of marked(rawData) to be rendered as html so we will use @html directive.

showPreview function get invoked on clicking the button which then assigns preview = true and hence the UI is re-rendered and the if condition is satisfied and hence the preview is shown.

Step 3:

Let's use the classes assigned in the above steps to style the app.

<script>
    import marked from 'marked';

    let rawData = '';
    let preview = false;

    function showPreview() {
        preview = true;
    }
</script>

<main class='main-container'>
    {#if preview}
        <div>
            {@html marked(rawData)}
        </div>
    {:else}
        <form class='form'>
            <textarea type='text' bind:value={rawData} class='markdown-input' placeholder='Enter your markdown content here...'/>
            <button class='show-btn' on:click={showPreview}>
             Show Preview
            </button>
    </form>
    {/if}
</main>


<style>
    .main-container {
        display: flex;
        flex-direction: column;
        padding: 1rem 0.6rem;
        margin: 0;
        height: 100%;
        width: 100%;
    }

.form {
        height: 100%;
        width: 95%;
}

    .markdown-input {
        width: 100%;
        height: 90%;
        border: unset;
        border: 1px solid #9c9a9a;
        padding: 0.8rem 1rem;
        border-radius: 10px;
        background: transparent;
    }

    .markdown-input::placeholder {
        font-size: 1.2rem;
        font-weight: 100;
        font-family: sans-serif;
        letter-spacing: 1px;
    }

    .markdown-input:focus {
        outline: unset;
    }

    .show-btn {
        width: 100%;
        padding: 0.6rem 1.5rem;
        background: transparent;
        font-weight: 300;
        font-size: 1.5rem;
        border-radius: 10px;
        border: 1px solid #9c9a9a;
    }

</style>

We have used vanilla CSS so it would be self-explanatory.

Step 4:

We can make it more intuitive by adding one more enhancement. Currently, once we click on show preview we can't go back to EDIT mode. Let's add one more function to assign preview = false when the preview area is double-clicked. We will rename the showPreview to togglePreview and assign preview = !preview.

<script>
    import marked from 'marked';

    let rawData = '';
    let preview = false;

    function togglePreview() {
        preview = !preview;
    }
</script>

<main class='main-container'>
    {#if preview}
        <div on:dblclick={togglePreview}>
            {@html marked(rawData)}
        </div>
    {:else}
        <form class='form'>
            <textarea type='text' bind:value={rawData} class='markdown-input' placeholder='Enter your markdown content here...'/>
            <button class='show-btn' on:click={togglePreview}>
             Show Preview
            </button>
    </form>
    {/if}
</main>

<div on:dblclick={togglePreview}> invokes togglePreview on double-clicking the div showing the preview.

That is it! We have built a markdown preview application using Svelte. Hope you like it and learn something new. Do leave your comments and suggestions to keep me motivated.

Thank you all for reading this.

29