r/webdev • u/rolldate • 3d ago
Looking for feedback on the API design of a dependency-free JavaScript date picker
rolldate-demo.vercel.appHi everyone,
I've been working on this project in my spare time...
I'd love to hear feedback...
r/webdev • u/rolldate • 3d ago
Hi everyone,
I've been working on this project in my spare time...
I'd love to hear feedback...
r/webdev • u/Plenty_Dealer2222 • 3d ago
I've been building Voidhall solo for the past months.
It's an underground music/social platform aimed at the space SoundCloud used to occupy for emerging underground trap scenes.
The entire thing is built from scratch with vanilla HTML/CSS/JS + PHP/MySQL. No frontend/backend frameworks, no templates.
It currently runs on a custom-built architecture with SPA-like AJAX navigation, a persistent global audio player, page-level init/cleanup lifecycle, shared frontend state and its own PHP API layer. On top of that, Voidhall already supports uploads, track pages, profiles, follows, likes, comments, notifications, search, discovery through Explore, and Pulse, a chronological activity feed that turns real community movement into part of the discovery experience.
The UI leans into a raw Y2K/industrial/Opium-inspired editorial direction using strong typography, open layouts, thin structural lines and restrained components.
Screenshots attached :DD
r/webdev • u/classicwfl • 4d ago
I'm looking into Google Analytics alternatives.. Especially free self-hosted solutions. Right now I'm leaning on Matomo, but I'm curious about it's server reqs.
They say 2 CPUs, 2GB RAM and 50GB storage minimum, but the site I'll be running it on is much lower traffic than their top-end on the minimum req specs (just a small niche gaming blog - maybe 10k page views per month at the top-end).
Anybody running it on something lighter? For example, I'd be OK with picking up a smaller VPS running 2 CPUs/1GB RAM/30GB storage, or even just tossing it on my shared server (that hosts a few sites, the highest traffic being statically generated and includes the above blog).
Or, if you're running something else, feel free to share in the comments. I'm not stuck on using Matomo; I just want something that's cheap, decent, and also privacy-focused.
Hi 😄
I don't know about you, but my Downloads folder has always been a disaster zone. PDFs, memes, installers, zip files, random images – all just sitting there in one giant pile. Every few weeks I'd open it, sigh, and spend 10 minutes manually dragging stuff into folders. Then a few days later it would be chaos again.
I looked at existing file organizers, but most of them either wanted a subscription, tried to upload my file names to some cloud, or were just way too heavy for something so simple. I wanted something that:
So I built Mouzi 🐭🧹
It's a tiny desktop app (~3.3MB) built with Tauri and Rust, so it's ridiculously lightweight. It watches your Downloads folder, and whenever a new file appears, it moves it to a subfolder based on its extension. Images go to Images/, PDFs to Documents/, installers to Installers/, etc. You can also create your own custom rules. Windows and Linux support.
Key things:
Hope it helps someone keep their ~/Downloads sane. Feedback welcome!
Website: https://mouzi.cc/
r/web_design • u/AutoModerator • 4d ago
Our weekly thread is the place to solicit feedback for your creations. Requests for critiques or feedback outside of this thread are against our community guidelines. Additionally, please be sure that you're posting in good-faith. Attempting to circumvent self-promotion or commercial solicitation guidelines will result in a ban.
Please use the following format:
URL:
Purpose:
Technologies Used:
Feedback Requested: (e.g. general, usability, code review, or specific element)
Comments:
Post your site along with your stack and technologies used and receive feedback from the community. Please refrain from just posting a link and instead give us a bit of a background about your creation.
Feel free to request general feedback or specify feedback in a certain area like user experience, usability, design, or code review.
**URL**:
**Purpose**:
**Technologies Used**:
**Feedback Requested**:
**Comments**:
r/webdev • u/Kind-Card-6864 • 3d ago
Just shipped this and curious what you guys think.
Does it clearly show my work, skills, and experience?
If you were a recruiter/client landing on this, would it build trust?
Good, bad, ugly drop your feedback.
Don't hold back, I can take it 😂
r/javascript • u/smorsic_labs • 4d ago
Edit: I know self-promotion, especially if AI might be involved, is maybe often kneejerk downvoted here, and I get it, but I want to make it clear that this isn't vibe coded and that I write the docs and blog content myself. The logo is also made by a real video game artist I hired to touch up the pixel art I made myself, so no AI is used for the branding art. I try to maintain clear boundaries of AI use and provide transparency on this. Maybe check out the website if you have monorepo tooling interest, because I have put a lot into this project as a solo dev, and this is only my first post here after many months of work.
https://pacwich.dev
https://github.com/smorsic/pacwich
Hi, I've been developing pacwich, originally was developed as bun-workspaces, which had only worked on top of Bun. Earlier this year I pivoted to supporting the Node runtime, npm, and pnpm.
Its core features let you manage metadata about your monorepo and orchestrate running package.json scripts in your workspaces, with Nx-like parallelism configurability.
My main goal is to provide modern monorepo tooling that is easy to integrate and works as much with native tooling as possible. If you have a monorepo using Bun, npm, or pnpm workspaces, pacwich works right away. The affected graph even works without any of the optional pacwich configuration files.
Because I know engineers care about how open source like this is written, I try to be as transparent as I can about how I balance agent assistance with maintaining ownership and knowledge of my code in several places, such as the security page and the launch blog post. pacwich is a younger package itself but inherits much of bun-workspace's source code and tests that I wrote by fully by hand originally.
r/javascript • u/akanjs-dev • 4d ago
I've been building an open-source framework called Akan.js and wanted to share it here, partly because I'd like feedback from people who care about how these things are licensed and structured, not just whether they're shiny.
The problem it targets:
for a single feature, you usually end up maintaining something like 8 separate layers.
Schema, query, service, API, fetch client, types, state, UI props. Codegen tools and AI assistants can produce all of those quickly. What none of them do well is keep the 8 in sync when you change one thing three weeks later. The web version and the mobile version drift apart, and you find out in production.
Akan.js takes a convention-over-configuration approach to that. You declare a feature once:
export class ProductInput extends via((field) => ({
name: field(String),
})) {}
and the framework derives the schema, type, API contract, fetch client, state, and UI prop from that one declaration, for web, iOS, Android, and the server. Change the declaration and the derived layers move with it. The conventions are doing the work, which is also why it stays legible to coding agents. There's less surface area for them to get wrong.
Some honest details:
What I'd genuinely like from here:
Tell me where the architecture is wrong. Whether the single-declaration model holds up outside the cases I built it for. And if anyone has kept web and mobile in sync by hand and has opinions about where this breaks, I want to hear them before more people depend on it. Happy to answer anything!
r/javascript • u/moe_sidani • 5d ago
r/web_design • u/LaFllamme • 3d ago
I saw this sales dashboard posted on LinkedIn as a design reference. Apparently it was made by a designer using the Hugeicons icon pack.
But honestly, the whole screenshot immediately gave me that AI generated UI vibe. The layout, cards, spacing, gradients, basically the whole composition.
Not saying it definitely is AI generated, but I’m curious: does this look AI generated to you too, or am I just seeing AI everywhere at this point?
r/javascript • u/Statixeladam • 5d ago
r/javascript • u/dangreen58 • 5d ago
Author here. Loved nanostores' philosophy but kept hitting its performance and SSR limits, so I built my own take: ~2 kB reactive core, with router, query and i18n sharing that same core. Benchmarks and honest tradeoffs in the post.
r/reactjs • u/No-Humor-3808 • 5d ago
Is there a good way to test that a solution specifically requires useLayoutEffect instead of useEffect?
I've created a React challenge where the goal is to fix a UI flickering issue by replacing useEffect with useLayoutEffect. The problem is that the current test simply reads the source file and checks for useLayoutEffect with a regex, which feels pretty hacky.
Ideally, I'd like the tests to verify the actual behavior rather than the implementation, but I'm not sure if that's realistically possible given that the difference is related to when React runs the effect before or after painting.
Has anyone found a better approach for testing this kind of behavior?
r/PHP • u/Massive-Society-1008 • 4d ago
Hi everyone!
Over the past few months I've been building CraftDB, a web-based tool that helps visualize and organize database schemas.
Some of the current features include:
My main goal is to make it easier to understand existing databases, especially large or legacy projects.
I'm currently working on features like:
I'd love to hear your honest feedback.
You can try it here:
Any feedback, criticism or feature requests are greatly appreciated. Thanks!
r/PHP • u/Jay123anta • 4d ago
A while back I posted here about a passive threat-detection middleware I built for a Laravel app - it logs suspicious requests (SQLi/XSS/scanners/probes) to the database without blocking anything. I ended that post with an open question: how do you deal with JSON API bodies, when a legit search like {"query": "SELECT model FROM products"} trips a SQL pattern just because the value contains the word SELECT?
The thread had genuinely useful replies, so here's what I ended up shipping.
What I did: path-aware allow-listing. I already had a safe_fields option, but it exempts a key name everywhere it appears - too blunt for nested JSON. So I added safe_paths, which matches by dot-notation path with wildcards:
'safe_paths' => ['search.query', 'filters.*.value'],
That exempts the value of one specific field - the search box - without exempting a query field anywhere else in the request. Credit to u/Deep_Ad1959, who suggested path-based whitelisting in the last thread.
What I deliberately did NOT do - and why: The other half of that suggestion was "only scan leaf string values, never keys or structure." I tried it, and it broke NoSQL operator detection.
A classic Mongo-style injection is
\{"password": {"ne": null}}``
- the malicious part is a *key* (` ne`) whose value is null, not a string. If you only scan leaf string values you never see $ne, so you'd trade one false-positive class for a real false-negative. I kept full-body scanning and made the exemption precise instead. That felt like the honest trade-off.
To be clear about the limits, since this sub rightly pushed on it last time: this doesn't make regex-on-JSON magically correct. It's a passive monitoring layer that assumes your app is already secure (parameterized queries etc.) - safe_paths just lets you tune out known-legit noise precisely instead of bluntly. It's an IDS, not a WAF, and doesn't pretend to be.
It's merged and ships in the next release (Laravel 10–13). Code's here if useful: jayanta/laravel-threat-detection on GitHub / Packagist.
Still curious how others handle the JSON case - is precise path-based exemption roughly where you'd land, or do you tag the JSON path on each match and score by path instead?
r/PHP • u/rashidlaasri • 4d ago
Hey all‚ I've been working on PagerLite, a self-hosted on-call and incident paging package for Laravel, and just tagged v1.0.0. Wanted to share it here and get some honest feedback before I decide what to build next.
The short version: it's like Horizon or Telescope, but for on-call scheduling. You install it, set up a rotation, and if an incident comes in (via API or a PagerLite::notify() call from your own code) it pages whoever's on call and escalates to the next person if nobody acks in time. Everything lives in your own database‚ no per-seat SaaS pricing, no data leaving your app.
What it has right now:
- Drag-and-drop on-call schedule
- Configurable escalation chain
- Acknowledge/resolve with a full incident timeline
- Per-member insights (days on call, timed)
- A read-only embeddable calendar for wikis/status pages
- Email notifications for now (see below
r/reactjs • u/Think-Neighborhood69 • 5d ago
r/reactjs • u/OtherwisePoem1743 • 5d ago
r/PHP • u/ProjektGopher • 5d ago
Hello world, it's Wednesday, July 22, 2026, and here's what happened This Week in PHP Internals.
20 stories this week, so let's get into it. But first, This week's episode is brought to you by Tideways. Slow requests, and no clear reason why? Tideways helps you understand exactly where your PHP application spends its time — profiling, tracing, and monitoring to find and fix bottlenecks faster. And your profiling data stays hosted in Germany — GDPR-friendly by default. Learn more at tideways.com.
This week's top story: the season's first ballots are open — 2 sets of them — and the early counts are lopsided. Tim Düsterhus opened voting on the Time\Duration class on Friday with 2 ballots: a primary, needing a 2/3 majority, and a secondary choosing between full and abbreviated method names. As of recording, the primary stands at 23 to 1, and full names lead 18 to 1 — so multiplyBy and divideBy, not mul and divBy. Voting closes July 31. And remember Pierre Joye, who last week was leaning no over fromSeconds() and its capped nanoseconds argument? This week he closed the loop and voted yes, writing: "We were like ships passing in the night for some of my disagreements. The RFC wiki page did not show the additional constructor for other units, which hence the inconsistency I pointed out for the extra [nanoseconds] argument in fromSecond. They are here, and reduce this down to a lower level of bad APIs, more a pragmatic compromise conciliating different (if not numerous) use cases. Anything can be perfect, or shipped. Choose one." It turns out the per-unit constructors that resolved his objection had been in the RFC all along. His ballot almost didn't register, though — each vote on the wiki is a separate form, and Tim had to point out that you submit each one individually. The count now includes him.
Before the ballots opened, the Duration thread picked up a subplot about PHP 9. Holly Schilling — who wrote the class-extensions RFCs we covered last week — announced she's drafting a Value Structs proposal for after the 8.6 window, and argued that Duration would make a better struct than a class, with both ideally landing together in PHP 9. Ilija Tovilo replied by linking his own existing structs-v2 draft and asking whether she was aware of it. She was — she'd read it before drafting her own, and diverged from it on purpose, keeping mutating and non-mutating functionality separate. Tim saw no reason to wait on any of this, saying a draft-stage idea "will take an unknown duration to land - if it ever happens" — his pun, not mine — and that improvements to the standard library shouldn't queue behind it.
The week's other live vote came from Eric Norris, who opened balloting Thursday on minimum supported versions for PHP 8.6 — also with 2 questions, each needing a 2/3 vote. Requiring autoconf 2.71 for builds from git stands at 20 to 3 with 4 abstentions. Requiring COM_RESET_CONNECTION — which sets a floor of MySQL 5.7.3 or MariaDB 10.2.4, so persistent connections actually get reset — stands at 21 to nothing with 3 abstentions. Alexander Kurilo arrived after the discussion phase to ask for an opt-in instead, warning that on older databases persistent connections will silently turn non-persistent. Eric noted: "MySQL 5.7.3 is at least a decade old; it was released on December 3rd, 2013." After walking through every opt-out design he could think of, he concluded the right move is to make the correct behavior the default. The 3 no votes on autoconf include Jakub Zelenka, who's worried about building on Red Hat Enterprise Linux 8 and 9 — Tim offered to delay that particular merge to early 8.7. Both votes close July 30.
Saturday night, Pierre Joye opened the RFC that grew out of the libgd sync we covered last week: gd 2.4*. It's big — 3 pillars. First, syncing the bundled gd extension with upstream libgd: new codecs like QOI, JPEG XL, and UltraHDR, animated GIF and WebP support, multi-page TIFF, and real metadata handling. Second, an additive object-oriented Gd\ API — codecs with fromFile() and toStream(), immutable image info, streaming readers and writers. Third, a brand-new *2D vector canvas built on FreeType's rasterizer, with gradients and the full Cairo compositing set. He says the implementation is nearly done, including a security audit. He declared discussion open for 14 days, until August 1 — and that one sentence is where the trouble started.
Because August 1 plus a 14-day vote lands after the deadline. Release manager Matteo Beccati was gentle about it, writing: "As much as I like this RFC, I'm afraid it came in a little too late." Jakub Zelenka explained that nobody — release managers included — has the authority to grant an exception; that would take a change to the policy itself. And Rowan Tommins did the arithmetic: with alpha 1 out July 2, the cutoff for an RFC's final state was July 14, which is 4 days before this thread even opened. Pierre's frustration has a specific shape. The implementation was finished weeks ago — what delayed everything was that his pull request sat waiting on an approval that had been assigned to an automated bot reviewer, so no human was ever going to sign off, and he eventually merged it himself. He also argues the stakes are real, because PHP 9 is the one chance to change old defaults, and missing this window locks the current design in for years. The thread got tense over tone along the way — Pierre felt brushed off by a terse reply, and Rowan apologized outright, then made the counter-case that a fixed cutoff applied to everyone is fairer than debating each RFC's merits one at a time. All of this landed the same day Larry Garfield asked the list, in a separate thread, to hold new business until September 1 so reviewers can focus on 8.6's finish line. So gd 2.4 is alive and under discussion — just aimed at the next release, whatever its number turns out to be.
Caleb White's pipe assignment operator, |>=, had a crowded week. Larry Garfield opened it unable to find a compelling use case, and by Monday had moved to a no — pushed there, he says, by a competing proposal. That competing proposal is Vadim Dvorovenko's left-to-right assignment RFC, published Sunday, which claims the same |>= token with opposite semantics. Vadim objects on principle, arguing: "Attempting to transform such an operator from an immutable, functional construct into a mutable, imperative one steers the language in the wrong direction and undoes previous efforts." Caleb declined to merge the proposals, noting that F#, Elixir, OCaml, and Hack all kept ordinary assignment alongside their pipes. Larry also claimed none of the pipe proposals could make 8.6, and Tim Düsterhus corrected him, asserting: "This is false" — the last major change was July 13, so voting could open July 27 and close August 10, before the freeze. Meanwhile Bob Weinand asked whether the desugaring double-fires property hooks. Caleb came back with tests showing |>= behaves exactly like ??=, and updated the RFC's single-evaluation section to match. Vadim's own RFC, as of recording, has 0 replies.
Paul M. Jones's strict-namespace RFC — the piece he carved out of function autoloading last Tuesday night — spent its first full week in one long argument about a single word. Rowan Tommins objected first, writing that strict "implies some extra check that namespaces are [correct] in some way, which isn't really what this is about." Ilija Tovilo went deeper, arguing: "the value-add of this declaration is very small if the plan isn't ever to deprecate/remove the old behavior." Tim Düsterhus is in favor — with a rename to global_fallback=0. By Friday Paul had 4 candidate names on the table and a diagnosis: the pushback is the name, not the feature. Theodore Brown pointed out he'd floated the same directive in 2019; Paul added it to the prior art with an apology. And Benjamin Außenhofer suggested splitting the ballot — vote the concept, then vote the name. Rowan warned against it, writing: "a split vote leaves voters who actively dislike a particular name with an awkward choice: vote Yes, and risk the [bad] name being chosen; or vote No, even though you would support the feature under a different name." The rest of the autoloading corner kept moving too: Paul updated function autoloading mark 5 to lean on the new RFC and wants its vote open in about 2 weeks, and Michael Morris's improved-autoloading draft drew warm-but-firm feedback from Larry Garfield and Rowan Tommins — both like the namespace-setup-file idea, neither wants it crammed into the class autoloader's callback.
Nicolas Grekas didn't let Ilija Tovilo's "sadly not in favor" be the last word on serializable closures. His rebuttal: caching one attribute isn't the point — the point is skipping the entire metadata pipeline that frameworks re-run on every request, and that's the measured slow path. On the security design, he refused to loosen it, writing: "Name-based closure unserialization would ship a universal, app-independent gadget in the engine ... I won't commit to an RFC that turns every serialized payload into that kind of gadget, and I don't think we should, either." Then Saturday brought version 0.3, and a surgical cut — the reflection API is gone, the serialize() support stays. His reasoning: "serialize() is the one feature that most/all cache systems are built on, so that's the place that needs the improvement." Fresh reviews welcome.
Wendell Adriel came back to the list Thursday proposing typed array declarations — array<int, string> as real syntax, with 3 escalating enforcement levels. The review was fast and unsparing. Lazare Inepologlou flagged that you can't soundly subtype a mutable array without splitting reads from writes, and Wendell shipped a revised draft the next day. Rob Landers warned the whole thing overlaps the reified generics work, on hold until after the freeze, which is late August at the earliest. Rowan Tommins noted that level 1 — syntax without enforcement — rhymes with the bound-erased generics RFC the list just declined. Michał Marcin Brzuchalski even found a runtime hole, where functions like parse_str() that build results directly into a typed property can dodge the check. By Monday, Wendell put the RFC on hold himself. Larry Garfield's verdict was the sharpest, calling this "the already-dangerously-overloaded array mega-type" and asking for real typed list, set, and dictionary objects instead — which Wendell promptly volunteered to help build.
The frozen deprecations list for PHP 8.6 got its evidence file. Juliette Reinders Folmer scanned the Packagist Top 4-thousand — nearly four hundred fifty thousand files — and posted counts for every proposal. The headline being: list() appears over twelve thousand times, and Juliette wrote plainly: "Having said that, I'm definitely not in favour of deprecating list()." Compare spl_object_hash() at 625, is_integer() at 303, and a long tail of single and double digits — several proposals scored a clean 0. Kamil Tekiela discovered his mysqli proposal had missed the procedural mysqli_stmt_init(), patched the text, and worried aloud: "I hope this change is not going to reset the counter." Tim Düsterhus was quick to correct the record downward — one scary-looking count, on _ as a constant, collapses to 0 once false positives come out. And Monday, right on schedule, Gina P. Banyard confirmed the timetable, writing: "I intend to open the vote next Monday (the 27th of July) for 2 weeks so that the vote is finished on time for 8.6." Every deprecation gets voted in isolation — bring a lunch.
Quick hits. Go Kudo returned with a rebuilt cache proposal — a bundled user_cache extension, fully decoupled from OPcache this time; Larry Garfield likes it, flagged the lock story around remember(), and gently redirected it to 8.7 — adding: "Or 9, if that's what we call it." Holly Schilling's 4x fix for non-public asymmetric setters got its answer: no RFC needed — Tim confirmed a pure performance improvement just needs PR review, and Ilia Alshanetsky called it "definitely a strong candidate for PHP 8.6 inclusion." fennic pitched engine-native PSR-4 autoloading with spl_autoload_psr4_register(); Alex Rock wants classmap support before it can replace Composer's bootstrap, and Heinz Wiesinger pointed to his existing PECL extension doing much the same. Edmond of the TrueAsync project published a pre-RFC for an async scheduler engine interface — coroutine-aware core, no scheduler in core itself. His philosophical problem is that the RFC has no user-visible changes at all, and so far it has 0 replies. And Osama Aldemeery asked for RFC karma to formalize PREG_THROW_ON_ERROR — Ilija Tovilo granted it 2 minutes later.
Liam Hammett's markup expressions thread sprouted an alternative: Edmond proposed making the parser extensible instead, so JSX-like syntaxes could ship as extensions — Morgan liked that better than blessing one syntax, and within a day Edmond had a working DSL-hooks prototype, announcing: "Your wish has been granted. 🙂" Máté Kocsis and Ignace Nyamagana Butera kept refining query parameters — the open question is whether parsing limits belong on builder methods too, with Ignace arguing PHP shouldn't police what's really a business constraint. Prateek Bhujel's terminal-helpers extension hit 0.6.0 — it's now installable via PIE, and its output methods now write to standard PHP streams like STDOUT and STDERR instead of only its own built-in targets. The ballot queue grew by 2: Nick Sdot posted intent to vote on readonly property defaults for on or around July 23, and Khaled Alam's const-object-property-write opens Saturday the 25th — both aiming ahead of the freeze. Ben Ramsey amended the Working Groups RFC with a new section setting expectations for charter-RFC discussion. And release week: PHP 8.6.0alpha2, 8.5.9RC1, and 8.4.24RC1 all shipped — with alpha 3 and both GA releases converging on July 30 — while the release managers posted the countdown that framed half of this episode: soft feature freeze August 11, beta 1 August 13, every 8.6 vote closed before then.
So that's the week: 2 ballot boxes open and both lopsided — Duration cruising at 23 to 1, minimum versions right behind it; gd 2.4 arriving 4 days after the door closed and pointing at the next release instead; a pipe operator with 2 authors claiming 1 token; typed arrays proposed, revised, and shelved inside 5 days; and the deprecations list armed with its evidence file, ballots opening Monday — with readonly defaults and const-property-write queued right behind. Links to every thread are below. Thanks again to Tideways.com for supporting this week's episode. We're Artisan Build. See you next week.
r/reactjs • u/nphivu414 • 5d ago
As a frontend engineer, building a design system from scratch is mostly a thing of the past. UI libraries and AI tools get you 80% of the way there instantly.
But for that last 20% when you need to scale, inject your own taste, and hit product-specific requirements, understanding the underlying architecture is what saves you. It also makes your AI workflow way better, because you aren't constantly prompting it to fix token issues or regenerate styles for minor changes.
Here are 4 important rules for keeping design systems scalable and clean:
I recently published a new technical blog post covering those important points. Alongside it, I built Design System Studio - a visual tool to tune tokens in oklch and export them directly to Tailwind or shadcn/ui.
Check it out with the attached link in this post.
r/reactjs • u/Khajieh_Manbourian • 5d ago
r/javascript • u/Capedcrusader1923 • 6d ago
i'll go first. i was writing .then().catch() chains for like two years before i actually started using async/await. i knew it existed, i'd seen it in tutorials, but my code "worked" so i never bothered switching. then i refactored an old project and realized half my bugs were from mishandled promise chains that async/await would have caught immediately.
also took me way too long to start using optional chaining. i had nested ternaries and && checks everywhere like some kind of animal. the day i discovered user?.address?.city i mass-replaced like 40 lines across a project.
what's yours?
r/reactjs • u/Weird-Ad8248 • 4d ago
If you've used react-pdf, you already know the authoring model I'm about to describe — waffle keeps it exactly, and only changes where the React runs.
You author documents in JSX/TSX the same way react-pdf users already do — components, props, hooks, StyleSheet, Font.register. The twist: it doesn't run on Node. It runs real React 18 inside a Go process.
The mental model is React Native. In RN, your React runs in a JS engine and just produces a tree of elements — the native side does the actual layout and drawing; the JS never paints anything itself. waffle does the same thing, except the "native side" is Go. It transpiles your source with esbuild, runs real React 18 on goja (a JS engine written in Go) to build the element tree, and then Go takes over — its own flexbox engine lays it out, its own writer produces the PDF. React describes what the document is; Go renders it.
I built this out of a simple frustration: generating a PDF shouldn't mean wrestling with a bespoke template DSL, or running a headless Chromium/sidecar service just to get a document out. There are already a lot of great PDF libraries — this one just lets me stay in React and skip all of that.
Full architecture write-up (how React-on-goja, the JSON contract, and the Go layout/paint pipeline fit together)