👋 Hello again! And welcome back to the "Coding Challenges with Friends" series. If you haven't read the previous article, I suggest you go read the first one for context. It'll only take a minute or two.
So what's the first challenge? It is to create a Markdown Editor with Live Preview using a library/framework that you've never used before, and to finish it in 24 hours (just to clarify, that's 24 hours total, not 24 hours straight).
It'd be great if you can partake of the challenge, and if you choose to do so, please do tag me on twitter, or use the tag #ccwf2021 here at dev.to.
Sounds good? Alright, let's take a look at how I approached the challenge, and let me walk you through some of my thought process during development.
Idea #1: All PHP, Sort Of
When we came up with this idea, the first approach that came to mind is to use a JavaScript library/framework like React or Vue. I mean, we're doing real-time stuff, so it makes sense, right? But I've been doing JavaScript development for so long now that I'm afraid I'll experience JavaScript fatigue soon. So I decided to do it in PHP. I also wanted to go back to PHP development since I've been away from it for quite some time, and I wanted to test the new tools available now.
This idea might sound a bit crazy, especially since one of the requirements is to have real-time markdown preview. And when people talk about real-time stuff on the web, PHP isn't usually part of the discussion.
Enter Laravel Livewire.
Livewire technically uses JavaScript in the background, but as a developer, you don't need to worry about it any more. You no longer need to write any JavaScript for your components yourself, you can stay in PHP from start to finish. The whole premise of Livewire is to "forget about JS" and to "trust the back-end", which is very promising. So let's dive into it.
The Setup
I used Laravel since Livewire is a Laravel package/library. And to parse the Markdown input, I used a package named Parsedown.
For the front-end, I used some vanilla JavaScript but only for the download and preview button (only visible on mobile), and then I just ported Github's markdown styles over.
And to further simplify things, I decided to deploy the app to Heroku. Just slap a Procfile in there and everything's set.
The Approach
Developing the app was pretty straightforward. I didn't use any advanced livewire features since I only needed basic data binding.
I then abstracted the markdown function to another class, which is not something that I really needed to do. I just like the idea of being able to replace Parsedown with a different markdown package without touching my main controller any more.
The code is so simple that I can just paste the whole controller here:
namespace App\Http\Livewire;
use Livewire\Component;
use App\Classes\MarkdownParser;
class MarkdownEditor extends Component
{
public $markdown;
public function render()
{
return view('livewire.markdown-editor', [
'markdown_preview' => MarkdownParser::parse($this->markdown)
]);
}
}
And that's it! Our app is Livewire Markdown Editor component is done.
So... How is it?
Well, the good news is that it works. I didn't write any Javascript except for the save and preview button (only visible on mobile), and it just magically works.
The bad news is this: Livewire's magic isn't really magic. The thing making the data-binding work is actually a series of small fetch requests to the PHP server facilitated by Livewire's JavaScript (injected via the @livewireScripts
directive). This means that the editing experience may greatly vary mainly based on your internet speed, and partly on how powerful your web hosting is. Testing it out on a "Slow 3G" connection made the issue all the more apparent.
This is a deal-breaker. I can't even start thinking about the other cool features I'd like to implement next. And just to clarify, I'm not taking away anything from Livewire. I still think it's a good alternative approach to building interactive web apps, just not for this one.
So what now? If we need actual real-time previews, then we need to handle the markdown stuff on the client side. I think you know where this is going...
Idea #2: Just JS, I mean "Just JS"
I know, I know. There are lots of ways to go about this, and a lot of libraries and frameworks to choose from like Preact/React, Svelte, Vue, etc. But as I was thinking about what framework to use, I realized that in reality, the only thing that I need help from a library for is parsing and converting markdown (because I'm not that smart to make my own parser). All the other stuff that these famous libraries come with are unnecessary in this context. And using any of those frameworks also meant setting up some build script. There are tools like create-react-app
that take care of those build systems for you, but if I can get away with not using any build scripts, I will. So I did.
I decided to just pick a well-reviewed markdown parser library and then wrote everything using Vanilla JavaScript. This did not only reduce the overall size of the app, but it also made deployment much easier. So let's take a look at it.
Looking Good, Ey?
Here's the main app's core function:
$textarea.addEventListener("keyup", (e) => {
$preview.innerHTML = marked(e.target.value);
localStorage.setItem("md-livewire", e.target.value);
});
Everything feels much more responsive when all the interaction's handled on the client side. This also allowed me to easily tap into the vast array of browser APIs. The one that I reached out for first is localStorage
.
The ability to persist data locally made the app feel like an actual product. In fact, I started writing this article in Notion, but proceeded to write the rest using this version of the app.
So what do we have so far? We have a markdown editor with real-time preview, we also have the ability to persist data locally (meaning, we can close/refresh the tab without losing our document), and we have the ability to download the markdown file if we want to. We can stop here, but I still have a lot of hours left from the 24-hour budget, and at this point, I haven't actually did or used anything new to me, so let's add some more features.
Idea #3: Vanilla, but with Icing on Top
I've wanted to create a Progressive Web App since 2018, but whenever I try to write one, something more urgent comes up. But I think now is the time. I think this project is perfect for it. And if you're not familiar with PWA, this page is a good starting point.
The first step to make the app progressive is to make it work offline. I mean, it's a markdown editor, I shouldn't need an internet connection to be able to write markdown documents. So I wrote a small service worker that adds all the necessary assets to cache storage upon registration. Once the service worker's installed, everything should be able to run offline.
The next step is to make the web app installable on desktop and mobile. For that, we just need a manifest.json
, add a reference to it along with a couple more meta tags on our markup like so:
<meta name="theme-color" content="#1e1e1e"/>
<link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico"/>
<link rel="apple-touch-startup-image" />
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png">
To install this on your phone, simply navigate to the site then add the page to your home screen, or if you're on the desktop, go to your settings and click the option that says "Install MDfy". Let's take a look at what we have now.
And while I'm at it, I decided to take a look at Lighthouse and tried to make everything green.
And with that, the challenge is done! 🎉 There's still a lot of features that I want to add to the app (and a couple of CSS bugs to fix on mobile), but as it is, our web app is already in a very good state. It can persist data locally, it's performant, it's installable, and more importantly it just works! Let's name it "MDfy" (em-di-fai)!
I'll definitely continue working on this, but I think this is a good stopping point for this challenge. And if you managed to reach this part of the post, then you're awesome and you have my thanks! I hope you learned a thing or two from this one. See you on the next challenge! 🙋♂️
Top comments (2)
By the way, here are the links to my friends' take on this challenge:
By Joimee (@oieeaaaa)
Live Demo: gallant-khorana-8248cf.netlify.app/
Source Code: github.com/oieeaaaa/i-use-md
By Ellice (@eacayan)
Live Demo: peaceful-leakey-2535c1.netlify.app/
Source Code: github.com/eacayan/md-svelte
I concede.