Hey DEV community! 👋
Have you ever found yourself spending hours dragging and dropping components in a GUI builder when all you wanted was to quickly create an admin panel? That was me, and I got tired of it. So I built something different.
The drag-and-drop frustration
As a backend developer, I love working with databases, implementing business logic, and building APIs. But every time I needed to create an internal tool or admin panel, I’d struggle with GUI-based builders like Retool.
Don’t get me wrong – Retool is powerful. But I kept running into issues:
- 🤦♂️ Moving components in the GUI would mysteriously break connections
- 😵 Reviewing changes in pull requests was nearly impossible
- 🤔 Teaching new team members the proprietary UI was time-consuming
- 🤖 AI coding assistants couldn’t help with GUI-based configurations
Plus, there was always this disconnect between my backend code and the frontend. I’d write a function in Go or TypeScript, then manually wire it up to a UI component through a confusing interface.
I just wanted to connect my backend functions directly to a UI!
The code-first dream
What if we could do this instead:
func usersPage(ui sourcetool.UIBuilder) error {
// Simple UI definition in Go
ui.Markdown("## Users")
// Input directly connected to your function
name := ui.TextInput("Name", textinput.WithPlaceholder("filter by name"))
// Call your existing backend function
users, err := listUsers(name)
if err != nil {
return err
}
// Display the results
ui.Table(users)
return nil
}
That’s it! No frontend code, no separate repository, no complex build setup. Just your backend code with some UI definitions.
This is exactly what Sourcetool does.
How it actually works
Sourcetool has a simple architecture with three main players:
- Your backend server (where your Go code runs)
- A Sourcetool server (handles auth and acts as a WebSocket bridge)
- The user’s browser (where the UI renders)
When a user interacts with the UI (clicks a button, types in a field), that event travels through WebSockets to your backend, where your code handles it. The UI then updates in real-time based on your response.
It takes just a few lines to set up:
func main() {
st := sourcetool.New(&sourcetool.Config{
APIKey: "YOUR_API_KEY",
Endpoint: "wss://your-sourcetool-instance",
})
// Register your pages
st.Page("/users", "Users", usersPage)
st.Page("/products", "Products", productsPage)
// Start the server
if err := st.Listen(); err != nil {
log.Fatal(err)
}
}
Perfect for the AI coding era
With tools like GitHub Copilot, ChatGPT, and Cursor changing how we write code, GUI builders feel increasingly outdated.
I can ask an AI to:
- “Add a form with name and email fields that calls the createUser function”
- “Add validation to this form”
- “Create a new page for displaying analytics”
And since it’s all just code, the AI can help directly – no screenshots or complex instructions needed!
The advantages over GUI builders
Here’s why a code-first approach works better for me:
- Git-friendly – Review PRs, track changes, and understand the history of your app
- Type-safe – Catch errors at compile time, not when users are trying to use your app
- Familiar tools – Use your existing IDE, linters, and code review workflows
- AI-friendly – Let AI assistants help you build and modify your tools
- No context switching – Stay in your backend language rather than jumping between environments
Current status
Right now, Sourcetool supports Go with TypeScript and Python SDKs coming soon. It’s entirely open-source under the Apache 2.0 license, so you can self-host and use it for free.
Here’s a quick example of creating a form:
func createUserPage(ui sourcetool.UIBuilder) error {
formUI, submitted := ui.Form("Create", form.WithClearOnSubmit(true))
name := formUI.TextInput("Name", textinput.WithRequired(true))
email := formUI.TextInput("Email", textinput.WithRequired(true))
if submitted {
_ = createUser(name, email)
}
return nil
}
Join the community!
I’ve open-sourced Sourcetool on GitHub and would love your feedback. It’s still early days, but if you share my frustrations with GUI builders, give it a try!
If you find this interesting, a GitHub star ⭐ would really help motivate further development! You can also reach out via Twitter/X DMs.
What do you think?
I’d love to hear your thoughts! Do you prefer GUI builders or code-first approaches? Have you experienced similar frustrations? Let me know in the comments below!
P.S. If you’re curious about how we built the backend-to-frontend bridge, check out this deep dive into WebSocket architecture.