
Recently, I just rewrite one of my application Stashbin from Next.js to GO. Though my main motivation of this migration was to learn GO and experimenting with HTMX. I also aiming to reduce the resource usage of my application and simplify the deployment process. Initially, Stashbin codebase are split into two seperate repository, one for the frontend that uses Next.js and another for the backend that already uses GO. The backend repository is just a REST API responsible for storing and retreiving data from the database.
With the migration, I want to combine both frontend and backend into a single repository and serve both the API and the web application from single application. This will simplify the deployment process and reduce the resource usage of the application.
I also mentioned on my previous post that I’m already so frustrated with React further more Next.js. I’m not saying that they were bad, I just don’t enjoy working with it anymore.
So, without further ado, let’s get started.
In this project, I’m planning to merge the backend and frontend into a single monolith application. Both of them will be served from a single web server in Go. I’m planning to use several libraries to get the project done:
Migrating the backend is quite simple as the previous backend is already written in Go. There will be only a few adjustments that I need to make since I’m planning to drop the ORM and use sqlx instead. All i need is to change the query done by the ORM to basic sql query.
This is the most challenging part of the migration. I need to rewrite the frontend from Next.js to Go (Templ syntax). I also use HTMX to replace React’s functionality, communicating with the backend, and updating the DOM.
Here’s some example of the Templ syntax for the main page:
package view
import "github.com/mrmissx/stashbin/view/layout"
import "github.com/mrmissx/stashbin/view/icons"
templ Home() {
@layout.BaseLayout() {
<form hx-post="/api/document" hx-trigger="keydown[keyCode === 83 && (ctrlKey || metaKey)]">
@layout.Header() {
@icons.Save()
}
<main>
<textarea
id="editor"
name="content"
placeholder="Write or paste, save and share the link..."
></textarea>
</main>
</form>
@layout.Footer()
<script>
document.addEventListener("keydown", (e) => {
if (e.keyCode == 83 && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey)) {
// prevent default Ctrl+S or Cmd+S
e.preventDefault();
return
}
}, false);
</script>
}
}With Templ, I can just import a layout and component like what React does with the @ syntax. I also use the hx-* attribute to communicate with the backend using HTMX API. In build time, Templ will generate a Go code that represents the HTML code above and compile it to the final binary file.
With the new monolith application, I can simplify the docker container deployments. I just need to build the application and run the binary file. I don’t need to build the frontend and backend separately and deploy them to different containers.
FROM golang:1.21-alpine AS build
WORKDIR /app
RUN apk update && \
apk add --no-cache nodejs npm
COPY go.mod go.sum package.json package-lock.json ./
RUN go mod download && \
go mod verify && \
go install github.com/a-h/templ/cmd/templ@latest \
npm install
COPY . .
RUN templ generate && \ # generate the Go code from the Templ syntax
npm run build # run the tailwindcss build
RUN go build -o stashbin # build the final binary
FROM alpine
WORKDIR /app
COPY --from=build /app/stashbin .
COPY --from=build /app/public ./public
CMD ["./stashbin"]This is the Dockerfile that I use to build the application. I use a multi-stage build to build the application and copy the final binary with all assets to the final image. We will get the most minimal image size as possible.
With this migration, I’m able to reduce the resource usage of my application, simplify the deployment process also improve the developer experience. Here’s some of the improvements that I can see:
The most significant improvement can be seen immediately from the docker image size. With just single codebase in GO I can bring the image size down from >100MB to just 16MB. Both of them are already the best optimization using docker build step without shiping the build dependencies.


With the new monolith application, I can reduce the resource usage of Stashbin especially the memory usage. The old application uses around 150MB of memory while the new GO application only uses 4MB of memory.
There are many other minor improvements that I can’t mention here like build time, developer experience, deployment speed, etc. I’m happy with the result and I’m looking forward to experiment more with GO and HTMX. If you want to contribute or reach me out, you can check the repository here.
See you in the next post! Cheers!
]]>
This post is about reason of me HATING React. I’ve been using React quite a while, starting from the transition of Class Component to Functional Component on version 16. Additionally, I coded my first React as a curious kid fascinated by React for a very simple reason:
The logo looks very cool!
I truly mean it; the logo is super cool. However, along the way as I use React, I often think that there is something wrong: ‘This doesn’t seem right’, ‘Why is this needed?’, ‘Why people are loving this?’, ‘is this going to the right way?’, ‘Isn’t this more complicated than just JavaScript’, etc. At this point, I consider that React only adds a burden to web development.

Without further ado, let me explain.
Wait, isn’t it great that React have a huge ecosystem?NO, NOT AT ALL
After quite a while using React and creating a few wrapper libraries for my own needs, I have a thought that the huge ecosystem of React are actually becomes a sign of the biggest React problem.
React isn’t compatible with Vanilla JavaScript
Most of frontend libraries are made with Vanilla JS. An example of library that you might frequently use is ”Chart.js”. But React is not compatible with Chart.js so here it comes ”React-chartjs-2” A wrapper library to work with Chart.js in React ecosystem. Oh you want to use ”three.js” for some cool 3D? you will need ”React-three/fiber”. In my case, I need to implement ”telegram-web-app”, not so fast, I have to create my own wrapper to be able to use it.
Can you see where this is going? The large ecosystem of React is formed because of React low compatibility with vanilla JavaScript libraries. React ecosystem is big because to use it you must make a library wrapper to be able to use it on React. It’s not an organic ecosystem that a new idea appear or something new created. By using React (and its supporting libraries), we unknowingly increase the complexity of the web development. The thing that might be more frustrating is that, it was so hard to keep maintaining the dependencies library to keep up to date. Eventually you will be faced with breaking change that you cannot longer update your dependencies or you have to break down the codebase to fix that obviously take your time.
export default function App() {
const [text, setText] = useState("")
console.log("rendered")
return <input type="text" value={text} onChange={(e) => setText(e.currentTarget.value)} />
}For everyone who ever dealt with user input (forms) must be familiar with those code above. Then what is the problem? When a user inputs, it will cause this whole component to re-render on EVERY INPUT!. Meaning that when someone types “hello world” the component will be re-rendered 11 times. You can try to run the code and count how many “rendered” will be printed on the console. Wait it becomes 12, How? yes on dev it will be rendered 12 times, I’ll also discuss that below.
Imagine someone unknowingly unleashing heavy computation on a component without useMemo. That computation will run on every component re-render. Not only that, The dependency array in your hooks (useEffect, useMemo, useCallback) are also checked in each re-render. It’s like a double whammy for performance pitfalls! I know that you can avoid this by some technique like what react-hook-form done, but remember that's an extra complexity.
Then whats a better manner to achieve reactivity. Most other framework like Svelte, Solid, Vue, Angular already adopt a better and faster way to achieve this reactivity. They are signals. What are signals, it is basically a state. But not like React when a state update it re-render the whole component, signal are built with the observer pattern. They have an observer that subscribe to the signal. Whenever the signal updates, only the one who subscribe to it will update. No need to diff the UI and re-render to perform update like what React’s done by adding the complexity to re-render the component under it.
Basically, React (JSX) is just a JavaScript file that supports html directly. When doing a conditional rendering, we must use the && operator or ternary operator. Yes this is very subjective, I personally doesn’t like this. Why? this thing can be a complete mess when managing or simply reading a component with the complex state and condition. Not to mention when someone comes up with the idea of using nested ternaries (oh god, I hate my life 😅 when reading those ternaries). Please get some life if you're one of those people 😂.
This is one of the things the most painful when developing a React app. Infinite re-renders/loop are all too common, often triggered by mistakes in setting up the dependency array in useEffect:
export default function App() {
const [count, setCount] = useState(0)
useEffect(() => {
setCount((c) => c + 1)
}, [count])
return <p>{count}</p>
}This snippet might appear harmless to React newcomers, but try to run it, and voilà—an infinite re-render unfolds.
OK, lets try another!
export default function App() {
const [count, setCount] = useState(0)
setCount((c) => c + 1)
return <p>{count}</p>
}Now what? your app will hangs with this code. Whats happening under the hood is:
Its not even updated on the DOM (it just hangs). The return line was never executed because the update process hasn’t been done yet, it keeps recursively calls update until the React throws the infinite re-render error.
Those bug are probably easy to find
But the thing is that when you developing a big project. You would probably use useContext and this where the pain starts. A state change on the context will most likely update a component far away from what are you currently on.
Again this burden doesn’t seem to happen on other frameworks that uses signals. Lets take an example with Solid
export default function App() {
const [count, setCount] = createSignal(0)
setCount((c) => c + 1)
return <p>{count()}</p>
}useState alike using signal
This syntax are nearly the same with React. Try to run it and yup you will get ‘1’. Lets try with the useEffect thing
export default function App() {
const [count, setCount] = createSignal(0)
createEffect(() => {
setCount((c) => c + 1)
})
return <p>{count()}</p>
}useEffect alike using signal
It would also display ‘1’. With signal approach it becomes more predictable than re-running the whole component to update a state.
What would you think the count would be on the browser when you develop this kind of code?
import { useEffect, useState } from "React"
export default function App() {
const [count, setCount] = useState(0)
useEffect(() => {
setCount((c) => c + 1)
}, [])
return <p>Rerender Count {count}</p>
}of course it would be 1
NO! it is 2. I can’t get it as well at first. How can it becomes 2? There is a black magic called React Strict Mode that Renders the component twice on development.
Why? React need to do this to check to make sure your app is safe and sound.
There are times that I was frustrated why React is calling my API twice and take like 15 minutes thinking it was a bug on my side and in the end realizing that it was the strict mode doing its thing. React create strict mode to patches and prevent you to create footguns like i mention earlier.
Others like Svelte, Solid, Vue doesn’t have this strict mode. They don’t need it because they don’t have those footguns in the first place.
Now here’s the devil,useEffect(), the realuseFootguns()in React.
This is the most abstraction I hate from React. I don’t know, why people doesn’t find this burden. For me, useEffect is the worst, most dirty and overengineered hooks in React. For context, this hooks exist to replace React lifecycle method componentWillMount, and componentWillUnmount also componentDidUpdate from class component (before React v16.7). It will be more sense and easier to read if they provide more hooks for those lifecycle like useMount and useUnmount but no, they stay with useEffect that handles completely different lifecycle.
Maybe most of you reading this might have a thought ”React is a library not a framework, of course you have to use other libraries to do other stuff”. I do take React as a framework. I can’t accept the fact that a library making a whole new rendering paradigm and more complex, where that complexity is not existent in the pure JavaScript ecosystem.
Indeed, all points I mentioned are very subjective to me. You can take it as my skill issue. Just to be clear, (unfortunately) I still uses React on my daily basis and in the near future as I mainly work with React. But the current frustrations is enough to make me think that React is heading to the wrong path of web development.
So… is React still worth learning? Yes, but do learn the basic first (HTML, CSS, JavaScript)!
We can’t deny that React is the most popular frontend framework. React is still the best choice for jobs in many companies. If you want to learn web development and wanting to land a job, React is the safest bet.
Beside that, do try other frameworks like Svelte. You will be shocked how much easier web development will be not have to think about React paradigm (VDOM, re-render, hooks, etc). I tried svelte (sveltekit) last year and it’s like the dream of development I looking for. Furthermore, if you wan’t to use pure HTML with the React-like you might like htmx. I personally love using it and have it on my personal project Stashbin. I migrate all the stack from Next.js to htmx, Go and Templ. Well, another blog post I guess.
That's all my viewpoint of the current React. This article has been in my draft since September 2023 and I really want to post it before the React v19 released 😅. I do love it when they finally announce the uses of compiler instead of heavily hinge by the runtime reactivity. Let's try it out once it is released.
Thank you for reading, See you on another blog post😉!
]]>As a developer, we’re constantly facing challenges in managing and deploying our projects or applications. Navigating the complexities of making application live on the internet can often seem daunting. In this article, we will explore an easy, streamlined flow for our application deployment, making the process not just manageable, but intuitive and efficient.
Here, we assume you’re already familiar with Docker basics and will focus on tools like Docker Compose, Portainer, and Nginx. Ready to streamline our projects from development to live environments? Let’s dive in.
There are several tools to help managing our projects.
Portainer is a lightweight management tool that simplifies the way we manage our Docker containers, Docker Swarm, and Kubernetes environments. It provides a user-friendly web interface that allows us to easily manage our Docker host or Swarm cluster. We’re using Portainer here to manage our containers stacks.
Nginx Proxy Manager is a tools that provide an easy way to manage an Nginx instance as a proxy host to our Application from the live internet. This tools also provide a free SSL using Let’s Encrypt if we do not own a custom SSL. This Nginx Proxy Manager will act as our application gateway to the public internet.
This git repo will be the manifest for all of our application. We can manage all docker compose file of our application here. We can upload it to private GitHub repo to have all automation versioning bump with Renovate Bot
A manifest repo file tree is
.
├── renovate.json
├── app 1
│ └── docker-compose.yml
└── app 2
└── docker-compose.ymlwith the renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recomended", "docker:enableMajor", ":dependencyDashboard"],
"prConcurrentLimit": 0,
"prHourlyLimit": 0,
"docker-compose": {
"fileMatch": ["(^|/)(?:docker-)?compose[^/]*\\.ya?ml$"]
}
}Follow Renovate docs for advanced configs e.g: private image registry (gcr, ghcr, etc)
With this manifest repository, we will receive a PR from renovate every update on the docker image tag.

Docker network is a feature of docker to define and manage network to connect a container. We have to connect all of our container in one network so that it will be able to communicate to each other.
docker network create my_networkTo spin up a Portainer instance is simple, all we need is a single docker-compose.yml file.
version: "3.9"
services:
portainer:
image: portainer/portainer-ce:alpine
container_name: portainer
privileged: true
volumes:
- ./portainer_data:/data
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
ports:
- 9443:9443
networks:
default:
name: my_network
external: trueNow that the installation is complete, we can log into Portainer dashboard on:
https://localhost:9443We might have to replace the localhost with the server IP’s.
Now we need to run the Nginx Proxy Manager for our reverse proxy. all we need also a single docker-compose.yml file.
version: "3.9"
services:
nginx:
image: jc21/nginx-proxy-manager:latest
container_name: nginx_proxy
restart: unless-stopped
ports:
- 80:80
- 81:81
- 443:443
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
default:
name: my_network
external: trueFollow the docs for more advanced instruction
There are 3 core ports exposed here. 80 and 443 is the gateway for public HTTP and HTTPS ports. 81 is used for the Nginx Proxy Manager dashboard.
We’ll need to log into the nginx dashboard via
http://localhost:81Again, we might have to replace the localhost with the server IP’s. It generate a default user [email protected] and password changeme. You will be asked to modify this at first.
After running Portainer and Nginx Proxy Manager, we need to setup our DNS to point to our server. I’m using cloudflare here for the example.

Setup both desired Nginx and Portainer domain/subdomain.
On the nginx proxy earlier, go to Proxy Host and add a new one
Nginx Host

Insert our domain name we setup earlier on Cloudflare and forward it to localhost with the port 81 as the dashboard we currently in are on port 81. Here we can also tell Nginx Proxy Manager to generate a new SSL.
Portainer Host

Point this for the container name running Portainer. If you follow my compose file the name is portainer, so the hostname will be portainer. This is why we need to create a global docker network as Nginx needs to communicate with the app container.
After saving both of the host, we can access our dashboard by the domain we configure. and from now on, no longer need to use the Server IP’s to go to the dashboard.
To deploy our application, we need to add it to our manifest repository.
.
├── renovate.json
├── nginx
│ └── docker-compose.yml
├── portainer
│ └── docker-compose.yml
└── your application
└── docker-compose.ymlDon’t forget to add the Network config on our compose file of the application!
networks:
default:
name: my_network
external: trueThen we need to add that application on the Portainer dahsboard. Go to environment -> stacks -> add stacks
Fill the name of our stack then choose ‘Repository’ for the build method. Enter all of the information needed and don’t forget to turn on the GitOps updates. With this setting, Portainer will send a polling to check updates of the compose file. We don't need to setup manually again, Portainer will automate all the deploying process.

Deploy the stack when all done.
After our container boots up on Portainer, all we need is assigning a domain for our application. Refer to step 5 first to add it on the DNS records. Then we need to configure the proxy host on Nginx Proxy Manager.

That’s all, it was a bit complicated at first but it is worth the time when there is a version update. All we need is merge the version bump from Renovate Bot and Portainer will spin it up automatically.
With the docker network we setup earlier, we no longer need to expose a port on each container. All communication cross-container are handled by the docker Network as long as the container are connected to the same docker network.
We can safely remove the port config on Portainer and Nginx Proxy Manager except the 80:80 and 443:443 as it now become the gateway for all application we have to the public internet.
In this article, we have learned how to streamline an application delivery using docker compose, Portainer and Nginx Proxy Manager.
It’s not a one-size-fits-all solution but rather a practice that can be adapted to our specific needs. Not as powerful as Kubernetes, but this software delivery pipeline are more than enough for small to middle scale application.
That’s it for this article. Thank you for reading!
]]>React.js adalah sebuah library JavaScript untuk membuat UI. React pertama kali dikembangkan oleh tim Facebook pada tahun 2013. Saat ini, React menjadi library frontend yang paling populer. Oke, kita mulai. Apa saja konsep dasar dari React.js?
Sebenarnya, SPA bukan fitur eksklusif pada React, tapi kita harus tahu apa itu SPA. Single Page Application ini bukan berarti aplikasi kita hanya memiliki satu halaman (route) /index ya… Lalu, SPA itu apa?
SPA ini adalah sebuah implementasi Web App yang saat sebuah halaman dibuka dia hanya akan melakukan load Document satu kali. Selanjutnya, ketika ada perubahan UI, semuanya akan dilakukan dengan menggunakan JavaScript API tanpa perlu melakukan load halaman/document lagi.
JSX adalah Sebuah extention dari JavaScript yang memungkinkan untuk menuliskan HTML pada JavaScript. Mungkin sudah banyak yang tau document.createElement pada JS. Nah dengan JSX pembuatan element dapat langsung ditulis pada file JavaScript tanpa createElement()
const el = document.createElement("h1")
el.innerHTML = "Hello World"
const el = <h1>Hello World</h1>
dari kode di atas bisa kita lihat, untuk membuat sebuah tag h1 dengan isi Hello World sangat mudah dan cepat pada JSX dibandingkan dengan JavaScript.
Pada lingkungan React ada 2 konsep DOM yaitu Real DOM (DOM pada browser) dan Virtual DOM (representasi Real DOM yang dikelola oleh React).
Virtual DOM adalah sebuah abstraksi dari DOM (Document Object Model). Karena proses manipulasi Real DOM itu lambat, hadirlah Virtual DOM. Proses manipulasi DOM pada Virtual DOM ini lebih cepat karena perubahan hanya terjadi pada memori, bukan di tampilan browser.
Lalu bagaimana cara Virtual DOM untuk mengubah tampilan browser?
Ketika sebuah state berubah pada Virtual DOM, React akan membandingkan dengan versi Virtual DOM sebelumnya. Ketika ditemukan perbedaan, maka selanjutnya React akan melakukan re-render komponen yang berubah pada Real DOM.
Dalam menggunakan React dapat dipastikan kita akan menggunakan kedua ini. State dan props berfungsi untuk mengelola data pada komponent.
State adalah sebuah data yang merepresentasikan sebuah keadaan. Mudahnya ini adalah sebuah variabel yang dapat berubah seiring dengan waktu dan interaksi dari user. Ketika state berubah, React akan secara otomatis melakukan re-render pada komponen tempat state itu berada
Props atau properties ini memiliki definisi yang sama dengan parameter pada sebuah fungsi. Props adalah sebuah data yang dikirimkan dari komponen parent-nya.
function sum(a, b) {
// di sini a dan b adalah sebuah parameter yang digunakan dalam fungsi sum
return a + b
}
sum(2, 5)function Sum(a, b) {
// fungsi sum memiliki props a dan b yang digunakan untuk menampilkan komponen
return (
<p>
{a} ditambah {b} adalah {a + b}
</p>
)
}
function App() {
return <Sum a={2} b={5} />
}Nah, ini salah satu konsep React.js yang mungkin agak sulit dipahami untuk orang yang baru terjun ke dunia React. Oke, kenapa ada hooks dan apa itu?
Hooks adalah sebuah fitur yang lahir akibat pergantian model komponen React yang awalnya Class Component beralih ke Functional Component. Hooks adalah sebuah fungsi JavaScript untuk membantu mengelola state dan lifecycle dari sebuah komponen React. Ada banyak macam dari Hooks pada React, tetapi 5 ini adalah yang paling banyak digunakan
seperti namanya ‘state’, hook ini digunakan untuk mendeklarasikan sebuah state pada komponen. Ketika state ini berubah, react akan melakukan re-render pada komponen dimana state ini berada. Ingat hanya perubahan state yang dapat memicu re-render. Ketika ada perubahan variabel biasa (let) react tidak akan melakukan re-render.
Mungkin ini salah satu hook yang cukup sulit dideskripsikan karena memilik banyak sekali use case. Hook ini sangat sering digunakan pada komponen. Meskipun begitu, banyak yang tidak tahu behaviornya secara penuh.
Hook ini memungkinkan kita untuk menambahkan ‘side effect’ pada komponen seperti fetching data, atau berinteraksi dengan DOM.
Masih bingung? mudah-mudahan alasan kenapa hook ini dibuat akan memudahkan untuk dipahami.
useEffect dibuat untuk menggantikan method lifecycle pada class component React yaitu componentWillMount, componentDidMount, dan componentWillUnmount. selain tiga tadi, hook ini juga digunakan sebagai pengganti componentDidUpdate.
Artinya, useEffect akan dijalankan ketika sebuah komponen di-mount (di-render) pada Real DOM, serta saat komponen akan di-unmount (dihapus dari Real DOM). Kita juga dapat mengatur agar useEffect dijalankan ketika ada sebuah state pada komponen yang berubah.
Hook ini bertujuan untuk melakukan memoisasi sebuah function yang terdapat di dalam komponen. Ketika react melakukan re-render, semua kode di dalam komponen akan dijalankan kembali, nah useCallback ini menghindari sebuah fungsi dibuat ulang ketika re-render.
Hook ini mirip dengan useCallback. Bedanya, useMemo digunakan untuk memoisasi sebuah value. Misalnya, kita memiliki ekspresi dengan komputasi besar, hasilnya dapat dimasukkan ke dalam useMemo untuk menghindari eksekusi pada setiap re-render komponen karena setiap render, react akan seluruh isi dari komponen yang di-rerender.
Hook ini berfungsi untuk memberikan konteks pada komponen. konteks ini dapat diibaratkan sebuah global state yang dapat diakses oleh komponen children tanpa perlu melakukan passing props.
Sekian pembahasan konsep dasar React.JS. Semoga membantu kalian yang baru terjun ke dunia React 🚀.
]]>