From db906fd85ac68b6f9ad63ff4ea5b2b5fdf3d44ec Mon Sep 17 00:00:00 2001 From: Mats Bosson Date: Sun, 29 Mar 2026 02:35:57 +0700 Subject: [PATCH] Fix linting config and package fields - Replace .eslintrc.cjs with eslint.config.mjs (ESLint 9 flat config) using direct eslint-plugin-solid + @typescript-eslint/parser approach - Add @typescript-eslint/parser to root devDependencies - Add main/module/types top-level fields to packages/core/package.json - Add resolve.conditions to packages/core/vite.config.ts - Create packages/core/tsconfig.test.json for test type-checking - Remove empty paths:{} from packages/core/tsconfig.json --- .eslintrc.cjs | 19 - .firecrawl/apis-for-ai.md | 2154 ++++++++++++ .firecrawl/beyond-bespoke-ui.md | 455 +++ .firecrawl/byteiota-vite8.md | 244 ++ .firecrawl/corvu-dynamic.md | 112 + .firecrawl/corvu-overview.md | 15 + .firecrawl/corvu-state.md | 125 + .firecrawl/corvu-styling.md | 91 + .firecrawl/csszone-anchor.md | 79 + .firecrawl/devproportal-radix-react-aria.md | 382 +++ .firecrawl/dewidar-headless-patterns.md | 224 ++ .firecrawl/json-render.md | 597 ++++ .firecrawl/kobalte-dialog.md | 263 ++ .firecrawl/kobalte-intro.md | 29 + .firecrawl/kobalte-polymorphism.md | 355 ++ .firecrawl/kobalte-select.md | 1464 ++++++++ .firecrawl/kobalte-ssr.md | 10 + .firecrawl/kobalte-styling.md | 315 ++ .firecrawl/logrocket-ai-shadcn.md | 381 +++ .firecrawl/logrocket-headless-alternatives.md | 1361 ++++++++ .firecrawl/logrocket-headless-ui.md | 934 +++++ .firecrawl/openui-overview.md | 130 + .firecrawl/openui-spec.md | 289 ++ .firecrawl/pkgpulse-bundling.md | 361 ++ .firecrawl/pkgpulse-floating.md | 705 ++++ .firecrawl/pkgpulse-linting.md | 305 ++ .firecrawl/pkgpulse-packagemgr.md | 256 ++ .firecrawl/pkgpulse-reactaria-radix.md | 377 +++ .firecrawl/pkgpulse-shadcn-baseui-radix.md | 372 ++ .firecrawl/pkgpulse-testing.md | 302 ++ .firecrawl/pkgpulse-validation.md | 290 ++ .firecrawl/shadcn-claude-settings.md | 252 ++ .firecrawl/shadcn-why-ai-loves.md | 149 + .firecrawl/solid-primitives-context.md | 151 + .firecrawl/solidjs-ui-libs.md | 281 ++ .firecrawl/stanza-compound-components.md | 536 +++ .firecrawl/state-of-react-2025.md | 1027 ++++++ .firecrawl/ui-lab-ai-integration.md | 239 ++ .firecrawl/visual-hallucination-gap.md | 225 ++ .firecrawl/vxd-compound-component.md | 226 ++ .../plans/2026-03-28-foundation.md | 2517 ++++++++++++++ .../specs/2026-03-28-pettyui-design.md | 1028 ++++++ eslint.config.mjs | 22 + package.json | 1 + packages/core/package.json | 3 + packages/core/tsconfig.json | 3 +- packages/core/tsconfig.test.json | 9 + packages/core/vite.config.ts | 3 + pnpm-lock.yaml | 2994 +++++++++++++++++ 49 files changed, 22641 insertions(+), 21 deletions(-) delete mode 100644 .eslintrc.cjs create mode 100644 .firecrawl/apis-for-ai.md create mode 100644 .firecrawl/beyond-bespoke-ui.md create mode 100644 .firecrawl/byteiota-vite8.md create mode 100644 .firecrawl/corvu-dynamic.md create mode 100644 .firecrawl/corvu-overview.md create mode 100644 .firecrawl/corvu-state.md create mode 100644 .firecrawl/corvu-styling.md create mode 100644 .firecrawl/csszone-anchor.md create mode 100644 .firecrawl/devproportal-radix-react-aria.md create mode 100644 .firecrawl/dewidar-headless-patterns.md create mode 100644 .firecrawl/json-render.md create mode 100644 .firecrawl/kobalte-dialog.md create mode 100644 .firecrawl/kobalte-intro.md create mode 100644 .firecrawl/kobalte-polymorphism.md create mode 100644 .firecrawl/kobalte-select.md create mode 100644 .firecrawl/kobalte-ssr.md create mode 100644 .firecrawl/kobalte-styling.md create mode 100644 .firecrawl/logrocket-ai-shadcn.md create mode 100644 .firecrawl/logrocket-headless-alternatives.md create mode 100644 .firecrawl/logrocket-headless-ui.md create mode 100644 .firecrawl/openui-overview.md create mode 100644 .firecrawl/openui-spec.md create mode 100644 .firecrawl/pkgpulse-bundling.md create mode 100644 .firecrawl/pkgpulse-floating.md create mode 100644 .firecrawl/pkgpulse-linting.md create mode 100644 .firecrawl/pkgpulse-packagemgr.md create mode 100644 .firecrawl/pkgpulse-reactaria-radix.md create mode 100644 .firecrawl/pkgpulse-shadcn-baseui-radix.md create mode 100644 .firecrawl/pkgpulse-testing.md create mode 100644 .firecrawl/pkgpulse-validation.md create mode 100644 .firecrawl/shadcn-claude-settings.md create mode 100644 .firecrawl/shadcn-why-ai-loves.md create mode 100644 .firecrawl/solid-primitives-context.md create mode 100644 .firecrawl/solidjs-ui-libs.md create mode 100644 .firecrawl/stanza-compound-components.md create mode 100644 .firecrawl/state-of-react-2025.md create mode 100644 .firecrawl/ui-lab-ai-integration.md create mode 100644 .firecrawl/visual-hallucination-gap.md create mode 100644 .firecrawl/vxd-compound-component.md create mode 100644 docs/superpowers/plans/2026-03-28-foundation.md create mode 100644 docs/superpowers/specs/2026-03-28-pettyui-design.md create mode 100644 eslint.config.mjs create mode 100644 packages/core/tsconfig.test.json create mode 100644 pnpm-lock.yaml diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 8314914..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,19 +0,0 @@ -/** @type {import('eslint').Linter.Config} */ -module.exports = { - root: true, - parser: "@typescript-eslint/parser", - parserOptions: { - ecmaVersion: 2020, - sourceType: "module", - }, - plugins: ["solid"], - extends: ["plugin:solid/typescript"], - rules: { - // Only Solid-specific rules — everything else handled by Biome - "solid/reactivity": "error", - "solid/no-destructure": "error", - "solid/prefer-for": "warn", - "solid/no-react-deps": "error", - "solid/no-react-specific-props": "error", - }, -}; diff --git a/.firecrawl/apis-for-ai.md b/.firecrawl/apis-for-ai.md new file mode 100644 index 0000000..3e9ff2a --- /dev/null +++ b/.firecrawl/apis-for-ai.md @@ -0,0 +1,2154 @@ +![Revisit consent button](https://cdn-cookieyes.com/assets/images/revisit.svg) + +We value your privacy + +We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking "Accept All", you consent to our use of cookies. + +CustomiseReject AllAccept All + +Powered by [Visit CookieYes website](https://www.cookieyes.com/product/cookie-consent/?ref=cypbcyb&utm_source=cookie-banner&utm_medium=fl-branding) + +Customise Consent Preferences![](https://cdn-cookieyes.com/assets/images/close.svg) + +We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below. + +The cookies that are categorised as "Necessary" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... Show more + +NecessaryAlways Active + +Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data. + +- Cookie + +\_\_eoi + +- Duration + +6 months + +- Description + +Description is currently not available. + + +- Cookie + +\_\_cf\_bm + +- Duration + +1 hour + +- Description + +This cookie, set by Cloudflare, is used to support Cloudflare Bot Management. + + +- Cookie + +\_cfuvid + +- Duration + +session + +- Description + +Cloudflare sets this cookie to track users across sessions to optimize user experience by maintaining session consistency and providing personalized services + + +- Cookie + +AWSALBCORS + +- Duration + +7 days + +- Description + +Amazon Web Services set this cookie for load balancing. + + +- Cookie + +li\_gc + +- Duration + +6 months + +- Description + +Linkedin set this cookie for storing visitor's consent regarding using cookies for non-essential purposes. + + +- Cookie + +\_\_hssrc + +- Duration + +session + +- Description + +This cookie is set by Hubspot whenever it changes the session cookie. The \_\_hssrc cookie set to 1 indicates that the user has restarted the browser, and if the cookie does not exist, it is assumed to be a new session. + + +- Cookie + +\_\_hssc + +- Duration + +1 hour + +- Description + +HubSpot sets this cookie to keep track of sessions and to determine if HubSpot should increment the session number and timestamps in the \_\_hstc cookie. + + +- Cookie + +AWSALB + +- Duration + +7 days + +- Description + +AWSALB is an application load balancer cookie set by Amazon Web Services to map the session to the target. + + +- Cookie + +\_\_Secure-YNID + +- Duration + +6 months + +- Description + +Google cookie used to protect user security and prevent fraud, especially during the login process. + + +- Cookie + +jwtOnAir + +- Duration + +1 month 1 day + +- Description + +Description is currently not available. + + +- Cookie + +jwt + +- Duration + +1 month 1 day + +- Description + +No description available. + + +- Cookie + +csrfToken + +- Duration + +1 month 1 day + +- Description + +Description is currently not available. + + +- Cookie + +\_octo + +- Duration + +1 year + +- Description + +No description available. + + +- Cookie + +logged\_in + +- Duration + +1 year + +- Description + +No description available. + + +- Cookie + +csrf\_token + +- Duration + +session + +- Description + +No description available. + + +- Cookie + +token\_v2 + +- Duration + +1 day + +- Description + +Description is currently not available. + + +- Cookie + +VISITOR\_PRIVACY\_METADATA + +- Duration + +6 months + +- Description + +YouTube sets this cookie to store the user's cookie consent state for the current domain. + + +- Cookie + +yt.innertube::nextId + +- Duration + +Never Expires + +- Description + +YouTube sets this cookie to register a unique ID to store data on what videos from YouTube the user has seen. + + +- Cookie + +yt.innertube::requests + +- Duration + +Never Expires + +- Description + +YouTube sets this cookie to register a unique ID to store data on what videos from YouTube the user has seen. + + +- Cookie + +session\_tracker + +- Duration + +session + +- Description + +This cookie is set by the Reddit. This cookie is used to identify trusted web traffic. It also helps in adverstising on the website. + + +- Cookie + +YSC + +- Duration + +session + +- Description + +YSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages. + + +- Cookie + +VISITOR\_INFO1\_LIVE + +- Duration + +6 months + +- Description + +A cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface. + + +- Cookie + +yt-remote-connected-devices + +- Duration + +Never Expires + +- Description + +YouTube sets this cookie to store the user's video preferences using embedded YouTube videos. + + +- Cookie + +ytidb::LAST\_RESULT\_ENTRY\_KEY + +- Duration + +Never Expires + +- Description + +The cookie ytidb::LAST\_RESULT\_ENTRY\_KEY is used by YouTube to store the last search result entry that was clicked by the user. This information is used to improve the user experience by providing more relevant search results in the future. + + +- Cookie + +yt-remote-device-id + +- Duration + +Never Expires + +- Description + +YouTube sets this cookie to store the user's video preferences using embedded YouTube videos. + + +- Cookie + +yt-remote-session-name + +- Duration + +session + +- Description + +The yt-remote-session-name cookie is used by YouTube to store the user's video player preferences using embedded YouTube video. + + +- Cookie + +yt-remote-fast-check-period + +- Duration + +session + +- Description + +The yt-remote-fast-check-period cookie is used by YouTube to store the user's video player preferences for embedded YouTube videos. + + +- Cookie + +yt-remote-session-app + +- Duration + +session + +- Description + +The yt-remote-session-app cookie is used by YouTube to store user preferences and information about the interface of the embedded YouTube video player. + + +- Cookie + +yt-remote-cast-available + +- Duration + +session + +- Description + +The yt-remote-cast-available cookie is used to store the user's preferences regarding whether casting is available on their YouTube video player. + + +- Cookie + +yt-remote-cast-installed + +- Duration + +session + +- Description + +The yt-remote-cast-installed cookie is used to store the user's video player preferences using embedded YouTube video. + + +- Cookie + +currency + +- Duration + +session + +- Description + +This cookie is used to store the currency preference of the user. + + +- Cookie + +cookieyes-consent + +- Duration + +1 year + +- Description + +CookieYes sets this cookie to remember users' consent preferences so that their preferences are respected on subsequent visits to this site. It does not collect or store any personal information about the site visitors. + + +- Cookie + +test\_cookie + +- Duration + +15 minutes + +- Description + +doubleclick.net sets this cookie to determine if the user's browser supports cookies. + + +- Cookie + +pxcts + +- Duration + +session + +- Description + +Description is currently not available. + + +- Cookie + +bStore + +- Duration + +Less than a minute + +- Description + +Description is currently not available. + + +- Cookie + +JSESSIONID + +- Duration + +session + +- Description + +New Relic uses this cookie to store a session identifier so that New Relic can monitor session counts for an application. + + +- Cookie + +\_pxvid + +- Duration + +1 year + +- Description + +PerimeterX sets this cookie to detect fraud and bot activity. + + +- Cookie + +\_px3 + +- Duration + +6 minutes + +- Description + +This cookie is set by the Bloomberg to protect the site from BOT attacks. + + +- Cookie + +cookietest + +- Duration + +session + +- Description + +The cookietest cookie is typically used to determine whether the user's browser accepts cookies, essential for website functionality and user experience. + + +- Cookie + +cf\_use\_ob + +- Duration + +1 minute + +- Description + +Cloudflare sets this cookie to improve page load times and to disallow any security restrictions based on the visitor's IP address. + + +Functional + +Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features. + +- Cookie + +BCTempID + +- Duration + +10 minutes + +- Description + +No description available. + + +- Cookie + +BCSessionID + +- Duration + +1 year 1 month 4 days + +- Description + +Blueconic sets this cookie as a unique identifier for the BlueConic profile. + + +- Cookie + +lidc + +- Duration + +1 day + +- Description + +LinkedIn sets the lidc cookie to facilitate data center selection. + + +- Cookie + +loid + +- Duration + +1 year 1 month 4 days + +- Description + +This cookie is set by the Reddit. The cookie enables the sharing of content from the website onto the social media platform. + + +- Cookie + +lang + +- Duration + +session + +- Description + +LinkedIn sets this cookie to remember a user's language setting. + + +Analytics + +Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc. + +- Cookie + +taboola\_session\_id + +- Duration + +session + +- Description + +This cookie is owned by trc.taboola.com and is used to create a temporary session ID to prevent duplicate recommendations from being displayed on the page. + + +- Cookie + +\_ga\_\* + +- Duration + +1 year 1 month 4 days + +- Description + +Google Analytics sets this cookie to store and count page views. + + +- Cookie + +\_ga + +- Duration + +1 year 1 month 4 days + +- Description + +Google Analytics sets this cookie to calculate visitor, session and campaign data and track site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognise unique visitors. + + +- Cookie + +\_\_hstc + +- Duration + +6 months + +- Description + +Hubspot set this main cookie for tracking visitors. It contains the domain, initial timestamp (first visit), last timestamp (last visit), current timestamp (this visit), and session number (increments for each subsequent session). + + +- Cookie + +hubspotutk + +- Duration + +6 months + +- Description + +HubSpot sets this cookie to keep track of the visitors to the website. This cookie is passed to HubSpot on form submission and used when deduplicating contacts. + + +- Cookie + +\_gh\_sess + +- Duration + +session + +- Description + +GitHub sets this cookie for temporary application and framework state between pages like what step the user is on in a multiple step form. + + +- Cookie + +\_clck + +- Duration + +1 year + +- Description + +Microsoft Clarity sets this cookie to retain the browser's Clarity User ID and settings exclusive to that website. This guarantees that actions taken during subsequent visits to the same website will be linked to the same user ID. + + +- Cookie + +\_clsk + +- Duration + +1 day + +- Description + +Microsoft Clarity sets this cookie to store and consolidate a user's pageviews into a single session recording. + + +- Cookie + +mp\_\*\_mixpanel + +- Duration + +Never Expires + +- Description + +Mixpanel sets this cookie to determine how users use the website so that a good user experience can be provided. + + +- Cookie + +browser\_id + +- Duration + +5 years + +- Description + +This cookie is used for identifying the visitor browser on re-visit to the website. + + +- Cookie + +li\_alerts + +- Duration + +1 year + +- Description + +LinkedIn sets this cookie to track impressions of LinkedIn alerts, such as the Cookie Banner and to implement cool-off periods for display of alerts. + + +- Cookie + +MR + +- Duration + +7 days + +- Description + +This cookie, set by Bing, is used to collect user information for analytics purposes. + + +Performance + +Performance cookies are used to understand and analyse the key performance indexes of the website which helps in delivering a better user experience for the visitors. + +- Cookie + +\_uetsid + +- Duration + +1 day + +- Description + +Bing Ads sets this cookie to engage with a user that has previously visited the website. + + +- Cookie + +\_uetvid + +- Duration + +1 year 24 days + +- Description + +Bing Ads sets this cookie to engage with a user that has previously visited the website. + + +Advertisement + +Advertisement cookies are used to provide visitors with customised advertisements based on the pages you visited previously and to analyse the effectiveness of the ad campaigns. + +- Cookie + +t\_pt\_gid + +- Duration + +1 year + +- Description + +Description is currently not available. + + +- Cookie + +muc\_ads + +- Duration + +1 year 1 month 4 days + +- Description + +Twitter sets this cookie to collect user behaviour and interaction data to optimize the website. + + +- Cookie + +guest\_id\_marketing + +- Duration + +1 year 1 month 4 days + +- Description + +Twitter sets this cookie to identify and track the website visitor. + + +- Cookie + +guest\_id\_ads + +- Duration + +1 year 1 month 4 days + +- Description + +Twitter sets this cookie to identify and track the website visitor. + + +- Cookie + +personalization\_id + +- Duration + +1 year 1 month 4 days + +- Description + +Twitter sets this cookie to integrate and share features for social media and also store information about how the user uses the website, for tracking and targeting. + + +- Cookie + +guest\_id + +- Duration + +1 year 1 month 4 days + +- Description + +Twitter sets this cookie to identify and track the website visitor. It registers if a user is signed in to the Twitter platform and collects information about ad preferences. + + +- Cookie + +bcookie + +- Duration + +1 year + +- Description + +LinkedIn sets this cookie from LinkedIn share buttons and ad tags to recognize browser IDs. + + +- Cookie + +\_gcl\_au + +- Duration + +3 months + +- Description + +Google Tag Manager sets this cookie to experiment advertisement efficiency of websites using their services. + + +- Cookie + +t\_gid + +- Duration + +1 year + +- Description + +Taboola sets this cookie by assigning a specific ID for attribution and reporting purposes and to tailor recommendations to the user. + + +- Cookie + +\_fbp + +- Duration + +3 months + +- Description + +Facebook sets this cookie to store and track interactions. + + +- Cookie + +MUID + +- Duration + +1 year 24 days + +- Description + +Bing sets this cookie to recognise unique web browsers visiting Microsoft sites. This cookie is used for advertising, site analytics, and other operations. + + +- Cookie + +bscookie + +- Duration + +1 year + +- Description + +LinkedIn sets this cookie to store performed actions on the website. + + +Uncategorised + +Other uncategorised cookies are those that are being analysed and have not been classified into a category as yet. + +No cookies to display. + +Reject AllSave My PreferencesAccept All + +Powered by [Visit CookieYes website](https://www.cookieyes.com/product/cookie-consent/?ref=cypbcyb&utm_source=cookie-banner&utm_medium=sl-branding) + +TNS + + +OK + + +As a JavaScript developer, what non-React tools do you use most often? + +✓ + +Angular + +0% + +✓ + +Astro + +0% + +✓ + +Svelte + +0% + +✓ + +Vue.js + +0% + +✓ + +Other + +0% + +✓ + +I only use React + +0% + +✓ + +I don't use JavaScript + +0% + +Thanks for your opinion! Subscribe below to get the final results, published +exclusively in our TNS Update newsletter: + + +SUBMIT + +[The New Stack Frontpage](https://thenewstack.io/) + +NEW! Try Stackie AI + + +LOGIN + +ARCHITECTURE + + +[Cloud Native Ecosystem](https://thenewstack.io/cloud-native/) [Containers](https://thenewstack.io/containers/) [Databases](https://thenewstack.io/databases/) [Edge Computing](https://thenewstack.io/edge-computing/) [Infrastructure as Code](https://thenewstack.io/infrastructure-as-code/) [Linux](https://thenewstack.io/linux/) [Microservices](https://thenewstack.io/microservices/) [Open Source](https://thenewstack.io/open-source/) [Networking](https://thenewstack.io/networking/) [Storage](https://thenewstack.io/storage/) + +ENGINEERING + + +[AI](https://thenewstack.io/ai/) [AI Engineering](https://thenewstack.io/ai-engineering/) [API Management](https://thenewstack.io/api-management/) [Backend development](https://thenewstack.io/backend-development/) [Data](https://thenewstack.io/data/) [Frontend Development](https://thenewstack.io/frontend-development/) [Large Language Models](https://thenewstack.io/llm/) [Security](https://thenewstack.io/security/) [Software Development](https://thenewstack.io/software-development/) [WebAssembly](https://thenewstack.io/webassembly/) + +OPERATIONS + + +[AI Operations](https://thenewstack.io/ai-operations/) [CI/CD](https://thenewstack.io/ci-cd/) [Cloud Services](https://thenewstack.io/cloud-services/) [DevOps](https://thenewstack.io/devops/) [Kubernetes](https://thenewstack.io/kubernetes/) [Observability](https://thenewstack.io/observability/) [Operations](https://thenewstack.io/operations/) [Platform Engineering](https://thenewstack.io/platform-engineering/) + +PROGRAMMING + + +[C++](https://thenewstack.io/c/) [Developer tools](https://thenewstack.io/developer-tools/) [Go](https://thenewstack.io/go/) [Java](https://thenewstack.io/java/) [JavaScript](https://thenewstack.io/javascript/) [Programming Languages](https://thenewstack.io/programming-languages/) [Python](https://thenewstack.io/python/) [Rust](https://thenewstack.io/rust/) [TypeScript](https://thenewstack.io/typescript/) + +CHANNELS + + +[Podcasts](https://thenewstack.io/podcasts/) [Ebooks](https://thenewstack.io/ebooks/) [Events](https://thenewstack.io/events/) [Webinars](https://thenewstack.io/webinars/) [Newsletter](https://thenewstack.io/newsletter/) [TNS RSS Feeds](https://thenewstack.io/rss-feeds/) + +THE NEW STACK + + +[About / Contact](https://thenewstack.io/about-and-contact-info/) [Sponsors](https://thenewstack.io/sponsors/) [Advertise With Us](https://thenewstack.io/sponsorship/) [Contributions](https://thenewstack.io/contributions/) + +[PODCASTS](https://thenewstack.io/podcasts/) [EBOOKS](https://thenewstack.io/ebooks/) [EVENTS](https://thenewstack.io/events/) [WEBINARS](https://thenewstack.io/webinars/) [NEWSLETTER](https://thenewstack.io/newsletter/) [CONTRIBUTE](https://thenewstack.io/contributions/) + +[ARCHITECTURE](https://thenewstack.io/its-time-to-build-apis-for-ai-not-just-for-developers/#) [ENGINEERING](https://thenewstack.io/its-time-to-build-apis-for-ai-not-just-for-developers/#) [OPERATIONS](https://thenewstack.io/its-time-to-build-apis-for-ai-not-just-for-developers/#) [PROGRAMMING](https://thenewstack.io/its-time-to-build-apis-for-ai-not-just-for-developers/#) + +[Cloud Native Ecosystem](https://thenewstack.io/cloud-native/) [Containers](https://thenewstack.io/containers/) [Databases](https://thenewstack.io/databases/) [Edge Computing](https://thenewstack.io/edge-computing/) [Infrastructure as Code](https://thenewstack.io/infrastructure-as-code/) [Linux](https://thenewstack.io/linux/) [Microservices](https://thenewstack.io/microservices/) [Open Source](https://thenewstack.io/open-source/) [Networking](https://thenewstack.io/networking/) [Storage](https://thenewstack.io/storage/) + +[Why WebAssembly won't replace Kubernetes but makes Helm more secure\\ +\\ +\\ +Mar 21st 2026 8:45am, by \\ +B. Cameron Gain](https://thenewstack.io/helm-webassembly-kubernetes-security/) + +[Why the ‘glorified host’ for AI is exactly the Kubernetes we need\\ +\\ +\\ +Mar 20th 2026 9:00am, by \\ +Danielle Cook](https://thenewstack.io/kubernetes-glorified-ai-host/) + +[Linux kernel scale is swamping an already-flawed CVE system\\ +\\ +\\ +Mar 20th 2026 4:30am, by \\ +Jed Salazar](https://thenewstack.io/linux-kernel-cve-system/) + +[Sampling: the philosopher's stone of distributed tracing\\ +\\ +\\ +Mar 19th 2026 8:00am, by \\ +Michele Mancioppi](https://thenewstack.io/distributed-tracing-sampling-opentelemetry/) + +[What is KubeVirt and why it’s growing\\ +\\ +\\ +Mar 17th 2026 9:00am, by \\ +Tiago Castro](https://thenewstack.io/kubevirt-live-migration-mayastor/) + +[Edera spent years calling KVM less secure. Here's why it changed its mind.\\ +\\ +\\ +Mar 25th 2026 2:22pm, by \\ +Steven J. Vaughan-Nichols](https://thenewstack.io/edera-adds-kvm-support/) + +[Minimus aims to solve one of open-source's long-festering problems\\ +\\ +\\ +Mar 24th 2026 3:00am, by \\ +Adrian Bridgwater](https://thenewstack.io/minimus-open-source-container-security/) + +[How to deploy Pi-Hole with Docker and stop ads on every device on your LAN\\ +\\ +\\ +Mar 23rd 2026 7:44am, by \\ +Jack Wallen](https://thenewstack.io/pihole-docker-network-adblocking/) + +[Chainguard has a fix for the open source packages your AI agents keep grabbing\\ +\\ +\\ +Mar 18th 2026 9:24am, by \\ +Darryl K. Taft](https://thenewstack.io/chainguard-repository-ai-agents/) + +[Chainguard thinks most DevOps teams are solving container security the hard way\\ +\\ +\\ +Mar 17th 2026 1:04pm, by \\ +Steven J. Vaughan-Nichols](https://thenewstack.io/chainguard-os-packages-containers/) + +[Scaling Btrfs to petabytes in production: a 74% cost reduction story\\ +\\ +\\ +Mar 18th 2026 5:00am, by \\ +Motiejus Jakštys](https://thenewstack.io/btrfs-petabyte-cost-reduction/) + +[The “files are all you need” debate misses what's actually happening in agent memory architecture\\ +\\ +\\ +Mar 13th 2026 5:00am, by \\ +Mikiko Bazeley](https://thenewstack.io/ai-agent-memory-architecture/) + +[With GridGain acquisition, MariaDB bets on in-memory computing and Apache Ignite\\ +\\ +\\ +Mar 10th 2026 6:47am, by \\ +Paul Sawers](https://thenewstack.io/with-gridgain-acquisition-mariadb-bets-on-in-memory-computing-and-apache-ignite/) + +[Moving AI apps from prototype to production requires enterprise-grade postgres infrastructure\\ +\\ +\\ +Mar 9th 2026 7:00am, by \\ +Meredith Shubel](https://thenewstack.io/ai-prototype-to-production-postgres/) + +[Why the "bible" of data systems is getting a massive rewrite for 2026\\ +\\ +\\ +Mar 4th 2026 5:00am, by \\ +Cynthia Dunlop](https://thenewstack.io/data-intensive-applications-rewrite-2026/) + +[Developers are coding to a moving target, and nobody knows where AI lands next\\ +\\ +\\ +Mar 3rd 2026 7:33am, by \\ +Adrian Bridgwater](https://thenewstack.io/developers-coding-moving-target-ai/) + +[Cloudflare’s new Markdown support shows how the web is evolving for AI agents\\ +\\ +\\ +Mar 2nd 2026 4:30am, by \\ +David Eastman](https://thenewstack.io/intent-engineering-ai-agents/) + +[React Server Components Vulnerability Found\\ +\\ +\\ +Dec 6th 2025 7:00am, by \\ +Loraine Lawson](https://thenewstack.io/react-server-components-vulnerability-found/) + +[Kubernetes at the Edge: Lessons From GE HealthCare’s Edge Strategy\\ +\\ +\\ +Nov 24th 2025 10:00am, by \\ +Vicki Walker](https://thenewstack.io/kubernetes-at-the-edge-lessons-from-ge-healthcares-edge-strategy/) + +[Building a Cloud-to-Edge Architecture Across 40K Global Locations\\ +\\ +\\ +Nov 20th 2025 10:00am, by \\ +Vicki Walker](https://thenewstack.io/building-a-cloud-to-edge-architecture-across-40k-global-locations/) + +[The operational gap is real, and it's getting wider\\ +\\ +\\ +Mar 26th 2026 8:00am, by \\ +Yevgeny Pats](https://thenewstack.io/closing-cloud-operational-gap/) + +[Why "automated" infrastructure might cost more than you think\\ +\\ +\\ +Feb 24th 2026 4:00am, by \\ +Justyn Roberts](https://thenewstack.io/automated-infrastructure-hidden-costs/) + +[Why 40% of AI projects will be canceled by 2027 (and how to stay in the other 60%)\\ +\\ +\\ +Feb 13th 2026 6:00am, by \\ +Alex Drag](https://thenewstack.io/agentic-ai-connectivity-platform/) + +[Durable Execution: Build reliable software in an unreliable world\\ +\\ +\\ +Feb 2nd 2026 3:23pm, by \\ +Charles Humble](https://thenewstack.io/temporal-durable-execution-platform/) + +[Terraform challenger Formae expands to more clouds\\ +\\ +\\ +Jan 28th 2026 6:00am, by \\ +Joab Jackson](https://thenewstack.io/terraform-competitor-formae-expands-to-more-clouds/) + +[Edera spent years calling KVM less secure. Here's why it changed its mind.\\ +\\ +\\ +Mar 25th 2026 2:22pm, by \\ +Steven J. Vaughan-Nichols](https://thenewstack.io/edera-adds-kvm-support/) + +[Linux kernel scale is swamping an already-flawed CVE system\\ +\\ +\\ +Mar 20th 2026 4:30am, by \\ +Jed Salazar](https://thenewstack.io/linux-kernel-cve-system/) + +[Scaling Btrfs to petabytes in production: a 74% cost reduction story\\ +\\ +\\ +Mar 18th 2026 5:00am, by \\ +Motiejus Jakštys](https://thenewstack.io/btrfs-petabyte-cost-reduction/) + +[Chainguard thinks most DevOps teams are solving container security the hard way\\ +\\ +\\ +Mar 17th 2026 1:04pm, by \\ +Steven J. Vaughan-Nichols](https://thenewstack.io/chainguard-os-packages-containers/) + +[Tromjaro is a free-trade Linux distribution with plenty to offer\\ +\\ +\\ +Mar 14th 2026 11:00am, by \\ +Jack Wallen](https://thenewstack.io/tromjaro-is-a-free-trade-linux-distribution-with-plenty-to-offer/) + +[Tetrate launches open source marketplace to simplify Envoy adoption\\ +\\ +\\ +Mar 11th 2026 10:52am, by \\ +Adrian Bridgwater](https://thenewstack.io/tetrate-built-on-envoy/) + +[OpenTelemetry roadmap: Sampling rates and collector improvements ahead\\ +\\ +\\ +Feb 24th 2026 11:00am, by \\ +B. Cameron Gain](https://thenewstack.io/opentelemetry-roadmap-sampling-rates-and-collector-improvements-ahead/) + +[Merging To Test Is Killing Your Microservices Velocity\\ +\\ +\\ +Dec 16th 2025 7:00am, by \\ +Arjun Iyer](https://thenewstack.io/merging-to-test-is-killing-your-microservices-velocity/) + +[IBM’s Confluent Acquisition Is About Event-Driven AI\\ +\\ +\\ +Dec 11th 2025 6:00am, by \\ +Joab Jackson](https://thenewstack.io/ibms-confluent-acquisition-is-about-event-driven-ai/) + +[Deploy Agentic AI Workflows With Kubernetes and Terraform\\ +\\ +\\ +Nov 26th 2025 9:00am, by \\ +Oladimeji Sowole](https://thenewstack.io/deploy-agentic-ai-workflows-with-kubernetes-and-terraform/) + +[Edera spent years calling KVM less secure. Here's why it changed its mind.\\ +\\ +\\ +Mar 25th 2026 2:22pm, by \\ +Steven J. Vaughan-Nichols](https://thenewstack.io/edera-adds-kvm-support/) + +[Broadcom donates Velero to CNCF — and it could reshape how Kubernetes users handle backup and disaster recovery\\ +\\ +\\ +Mar 24th 2026 8:38am, by \\ +B. Cameron Gain](https://thenewstack.io/broadcom-velero-cncf-kubernetes/) + +[Minimus aims to solve one of open-source's long-festering problems\\ +\\ +\\ +Mar 24th 2026 3:00am, by \\ +Adrian Bridgwater](https://thenewstack.io/minimus-open-source-container-security/) + +[China is winning the open source AI race — but a US company still controls everything underneath\\ +\\ +\\ +Mar 20th 2026 10:46am, by \\ +Paul Sawers](https://thenewstack.io/china-leads-open-ai-models/) + +[From pillars to platform: How open observability data is changing the industry\\ +\\ +\\ +Mar 20th 2026 6:00am, by \\ +Ted Young](https://thenewstack.io/open-observability-ai-platforms/) + +[How to deploy Pi-Hole with Docker and stop ads on every device on your LAN\\ +\\ +\\ +Mar 23rd 2026 7:44am, by \\ +Jack Wallen](https://thenewstack.io/pihole-docker-network-adblocking/) + +[Why flat Kubernetes networks fail at scale\\ +\\ +\\ +Mar 20th 2026 7:00am, by \\ +Reza Ramezanpour](https://thenewstack.io/kubernetes-network-security-hierarchies/) + +[GSMA Open Gateway offers developers one API for 300+ mobile networks\\ +\\ +\\ +Mar 4th 2026 10:26am, by \\ +Adrian Bridgwater](https://thenewstack.io/gsma-open-gateway-developers/) + +[How Homepage simplifies monitoring your self-hosted services\\ +\\ +\\ +Feb 6th 2026 8:00am, by \\ +Jack Wallen](https://thenewstack.io/how-homepage-simplifies-monitoring-your-self-hosted-services/) + +[S3 is the new network: Rethinking data architecture for the cloud era\\ +\\ +\\ +Feb 2nd 2026 4:00am, by \\ +Max Liu](https://thenewstack.io/tidb-x-open-source-database/) + +[Scaling Btrfs to petabytes in production: a 74% cost reduction story\\ +\\ +\\ +Mar 18th 2026 5:00am, by \\ +Motiejus Jakštys](https://thenewstack.io/btrfs-petabyte-cost-reduction/) + +[What is KubeVirt and why it’s growing\\ +\\ +\\ +Mar 17th 2026 9:00am, by \\ +Tiago Castro](https://thenewstack.io/kubevirt-live-migration-mayastor/) + +[S3 is the new network: Rethinking data architecture for the cloud era\\ +\\ +\\ +Feb 2nd 2026 4:00am, by \\ +Max Liu](https://thenewstack.io/tidb-x-open-source-database/) + +[Agoda’s secret to 50x scale: Getting the database basics right\\ +\\ +\\ +Jan 28th 2026 7:00am, by \\ +Cynthia Dunlop](https://thenewstack.io/agodas-secret-to-50x-scale-getting-the-database-basics-right/) + +[Chainguard EmeritOSS backs MinIO, other orphaned projects\\ +\\ +\\ +Jan 27th 2026 6:15am, by \\ +Steven J. Vaughan-Nichols](https://thenewstack.io/chainguard-emeritoss-backs-minio-other-orphaned-projects/) + +[AI](https://thenewstack.io/ai/) [AI Engineering](https://thenewstack.io/ai-engineering/) [API Management](https://thenewstack.io/api-management/) [Backend development](https://thenewstack.io/backend-development/) [Data](https://thenewstack.io/data/) [Frontend Development](https://thenewstack.io/frontend-development/) [Large Language Models](https://thenewstack.io/llm/) [Security](https://thenewstack.io/security/) [Software Development](https://thenewstack.io/software-development/) [WebAssembly](https://thenewstack.io/webassembly/) + +[Claude can now open apps, click buttons, and complete tasks on your Mac — but Anthropic says risks remain](https://thenewstack.io/claude-computer-use/) + +Mar 25th 2026 4:36am, by [Meredith Shubel](https://thenewstack.io/author/mshubel/ "Posts by Meredith Shubel") + +[Ai2 launches MolmoWeb, an open-source web agent](https://thenewstack.io/ai2-launches-molmoweb-an-open-source-web-agent/) + +Mar 24th 2026 9:07am, by [Frederic Lardinois](https://thenewstack.io/author/frederic-lardinois/ "Posts by Frederic Lardinois") + +[Four prompt engineering patterns every developer should know — and why "draw a cat" explains them all](https://thenewstack.io/prompt-engineering-for-developers/) + +Mar 23rd 2026 11:00am, by [Jessica Wachtel](https://thenewstack.io/author/jessica-wachtel/ "Posts by Jessica Wachtel") + +[China is winning the open source AI race — but a US company still controls everything underneath](https://thenewstack.io/china-leads-open-ai-models/) + +Mar 20th 2026 10:46am, by [Paul Sawers](https://thenewstack.io/author/paul-sawers/ "Posts by Paul Sawers") + +[Cursor's Composer 2 beats Opus 4.6 on coding benchmarks at a fraction of the price](https://thenewstack.io/cursors-composer-2-beats-opus/) + +Mar 19th 2026 8:39am, by [Frederic Lardinois](https://thenewstack.io/author/frederic-lardinois/ "Posts by Frederic Lardinois") + +[Why online stores keep showing the wrong products — and why tensors fix it](https://thenewstack.io/tensor-ranking-product-discovery/) + +Mar 25th 2026 6:00am, by [Tim Young](https://thenewstack.io/author/tim-young/ "Posts by Tim Young") + +[OpenClaw's biggest security flaw is why Jentic Mini exists](https://thenewstack.io/openclaw-is-a-security-mess-jentic-wants-to-fix-it/) + +Mar 25th 2026 5:00am, by [Darryl K. Taft](https://thenewstack.io/author/darryl-taft/ "Posts by Darryl K. Taft") + +[Why most AI projects fail after the demo actually works](https://thenewstack.io/ai-demo-to-production/) + +Mar 25th 2026 4:00am, by [Oladimeji Sowole](https://thenewstack.io/author/oladimeji-sowole/ "Posts by Oladimeji Sowole") + +[PwC's AI agents are now your consultants -- whether you're ready or not](https://thenewstack.io/pwcs-ai-agents-are-now-your-consultants-whether-youre-ready-or-not/) + +Mar 24th 2026 6:00am, by [Darryl K. Taft](https://thenewstack.io/author/darryl-taft/ "Posts by Darryl K. Taft") + +[Four prompt engineering patterns every developer should know — and why "draw a cat" explains them all](https://thenewstack.io/prompt-engineering-for-developers/) + +Mar 23rd 2026 11:00am, by [Jessica Wachtel](https://thenewstack.io/author/jessica-wachtel/ "Posts by Jessica Wachtel") + +[MCP is everywhere, but don't panic. Here's why your existing APIs still matter.](https://thenewstack.io/api-mcp-agent-integration/) + +Mar 23rd 2026 5:00am, by [Camille Crowell-Lee](https://thenewstack.io/author/camille-crowell-lee/ "Posts by Camille Crowell-Lee") and [Morgan Fine](https://thenewstack.io/author/morgan-fine/ "Posts by Morgan Fine") + +[Before you let AI agents loose, you’d better know what they’re capable of](https://thenewstack.io/risk-mitigation-agentic-ai/) + +Mar 12th 2026 1:22pm, by [Charles Humble](https://thenewstack.io/author/charles-humble/ "Posts by Charles Humble") + +[GSMA Open Gateway offers developers one API for 300+ mobile networks](https://thenewstack.io/gsma-open-gateway-developers/) + +Mar 4th 2026 10:26am, by [Adrian Bridgwater](https://thenewstack.io/author/adrian-bridgwater/ "Posts by Adrian Bridgwater") + +[Your AI strategy is built on layers of API sediment](https://thenewstack.io/ai-strategy-api-sediment/) + +Feb 17th 2026 9:37am, by [Charles Humble](https://thenewstack.io/author/charles-humble/ "Posts by Charles Humble") + +[Solving the Problems That Accompany API Sprawl With AI](https://thenewstack.io/solving-the-problems-that-accompany-api-sprawl-with-ai/) + +Jan 15th 2026 1:00pm, by [Heather Joslyn](https://thenewstack.io/author/hjoslyn/ "Posts by Heather Joslyn") + +[Backend Development in 2026: What's Changed, What Matters, and What to Learn Next](https://thenewstack.io/introduction-to-backend-development/) + +Mar 19th 2026 11:37am, by [TNS Staff](https://thenewstack.io/author/tns-staff/ "Posts by TNS Staff") + +[How To Get DNS Right: A Guide to Common Failure Modes](https://thenewstack.io/how-to-get-dns-right-a-guide-to-common-failure-modes/) + +Dec 24th 2025 8:00am, by [Sheldon Pereira](https://thenewstack.io/author/sheldon-pereira/ "Posts by Sheldon Pereira") and [Denton Chikura](https://thenewstack.io/author/denton-chikura/ "Posts by Denton Chikura") + +[Combining Rust and Python for High-Performance AI Systems](https://thenewstack.io/combining-rust-and-python-for-high-performance-ai-systems/) + +Dec 3rd 2025 1:00pm, by [Zziwa Raymond Ian](https://thenewstack.io/author/zziwa-raymond/ "Posts by Zziwa Raymond Ian") + +[How MCP Uses Streamable HTTP for Real-Time AI Tool Interaction](https://thenewstack.io/how-mcp-uses-streamable-http-for-real-time-ai-tool-interaction/) + +Aug 18th 2025 10:34am, by [Janakiram MSV](https://thenewstack.io/author/janakiram/ "Posts by Janakiram MSV") + +[A Backend for Frontend: Watt for Node.js Simplifies Operations](https://thenewstack.io/a-backend-for-frontend-watt-for-node-js-simplifies-operations/) + +Aug 14th 2025 6:00am, by [Loraine Lawson](https://thenewstack.io/author/loraine-lawson/ "Posts by Loraine Lawson") + +[Fivetran donates its SQLMesh data transformation framework to the Linux Foundation](https://thenewstack.io/fivetran-donates-sqlmesh-lf/) + +Mar 25th 2026 7:39am, by [Frederic Lardinois](https://thenewstack.io/author/frederic-lardinois/ "Posts by Frederic Lardinois") + +[Ex-Snowflake engineers say there's a blind spot in data engineering — so they built Tower to fix it](https://thenewstack.io/tower-python-data-pipelines/) + +Mar 15th 2026 7:00am, by [Paul Sawers](https://thenewstack.io/author/paul-sawers/ "Posts by Paul Sawers") + +[Why the "bible" of data systems is getting a massive rewrite for 2026](https://thenewstack.io/data-intensive-applications-rewrite-2026/) + +Mar 4th 2026 5:00am, by [Cynthia Dunlop](https://thenewstack.io/author/cynthiadunlop/ "Posts by Cynthia Dunlop") + +[How to clone a drive to an image with Clonezilla](https://thenewstack.io/how-to-clone-a-drive-to-an-image-with-clonezilla/) + +Mar 3rd 2026 1:00pm, by [Jack Wallen](https://thenewstack.io/author/jack-wallen/ "Posts by Jack Wallen") + +[Databases weren’t built for agent sprawl – SurrealDB wants to fix it](https://thenewstack.io/surrealdb-3-ai-agents/) + +Feb 24th 2026 2:07pm, by [Paul Sawers](https://thenewstack.io/author/paul-sawers/ "Posts by Paul Sawers") + +[WebMCP turns any Chrome web page into an MCP server for AI agents](https://thenewstack.io/webmcp-chrome-ai-agents/) + +Mar 17th 2026 11:50am, by [David Eastman](https://thenewstack.io/author/david-eastman/ "Posts by David Eastman") + +[Confluent adds A2A support, anomaly detection, and Queues for Kafka in major platform update](https://thenewstack.io/confluent-kafka-a2a-agents/) + +Mar 3rd 2026 10:21am, by [Jelani Harper](https://thenewstack.io/author/jelani-harper/ "Posts by Jelani Harper") + +[Google's Chrome browser moves to a two-week release cycle](https://thenewstack.io/chrome-two-week-releases/) + +Mar 3rd 2026 9:00am, by [Frederic Lardinois](https://thenewstack.io/author/frederic-lardinois/ "Posts by Frederic Lardinois") + +[Meta gave React its own foundation. But it's not letting go just yet.](https://thenewstack.io/react-foundation-open-source-governance/) + +Mar 3rd 2026 4:00am, by [Paul Sawers](https://thenewstack.io/author/paul-sawers/ "Posts by Paul Sawers") + +[The shift left hangover: Why modern platforms are shifting down to cure developer fatigue](https://thenewstack.io/shift-left-hangover-steve-corndell/) + +Jan 30th 2026 6:22pm, by [Steve Corndell](https://thenewstack.io/author/steve-corndell/ "Posts by Steve Corndell") + +[Why most AI projects fail after the demo actually works](https://thenewstack.io/ai-demo-to-production/) + +Mar 25th 2026 4:00am, by [Oladimeji Sowole](https://thenewstack.io/author/oladimeji-sowole/ "Posts by Oladimeji Sowole") + +[IBM, Red Hat, and Google just donated a Kubernetes blueprint for LLM inference to the CNCF](https://thenewstack.io/llm-d-cncf-kubernetes-inference/) + +Mar 24th 2026 8:20am, by [Steven J. Vaughan-Nichols](https://thenewstack.io/author/sjvn/ "Posts by Steven J. Vaughan-Nichols") + +[Andrej Karpathy's 630-line Python script ran 50 experiments overnight without any human input](https://thenewstack.io/karpathy-autonomous-experiment-loop/) + +Mar 14th 2026 5:00am, by [Janakiram MSV](https://thenewstack.io/author/janakiram/ "Posts by Janakiram MSV") + +["Self-healing" IT? HPE research explores how AI-trained models can catch silent infrastructure failures](https://thenewstack.io/hpe-self-healing-ai-infrastructure/) + +Mar 11th 2026 9:37am, by [Jennifer Riggins](https://thenewstack.io/author/jennifer-riggins/ "Posts by Jennifer Riggins") + +[How context rot drags down AI and LLM results for enterprises, and how to fix it](https://thenewstack.io/context-rot-enterprise-ai-llms/) + +Mar 9th 2026 9:00am, by [Todd R. Weiss](https://thenewstack.io/author/todd-r-weiss/ "Posts by Todd R. Weiss") + +[OpenClaw's biggest security flaw is why Jentic Mini exists](https://thenewstack.io/openclaw-is-a-security-mess-jentic-wants-to-fix-it/) + +Mar 25th 2026 5:00am, by [Darryl K. Taft](https://thenewstack.io/author/darryl-taft/ "Posts by Darryl K. Taft") + +[WebAssembly could solve AI agents' most dangerous security gap](https://thenewstack.io/webassembly-sandboxing-ai-agents/) + +Mar 24th 2026 9:01am, by [B. Cameron Gain](https://thenewstack.io/author/bruce-gain/ "Posts by B. Cameron Gain") + +[Minimus aims to solve one of open-source's long-festering problems](https://thenewstack.io/minimus-open-source-container-security/) + +Mar 24th 2026 3:00am, by [Adrian Bridgwater](https://thenewstack.io/author/adrian-bridgwater/ "Posts by Adrian Bridgwater") + +[What a security audit of 22,511 AI coding skills found lurking in the code](https://thenewstack.io/ai-agent-skills-security/) + +Mar 22nd 2026 7:00am, by [Darryl K. Taft](https://thenewstack.io/author/darryl-taft/ "Posts by Darryl K. Taft") + +[Why WebAssembly won't replace Kubernetes but makes Helm more secure](https://thenewstack.io/helm-webassembly-kubernetes-security/) + +Mar 21st 2026 8:45am, by [B. Cameron Gain](https://thenewstack.io/author/bruce-gain/ "Posts by B. Cameron Gain") + +[Why online stores keep showing the wrong products — and why tensors fix it](https://thenewstack.io/tensor-ranking-product-discovery/) + +Mar 25th 2026 6:00am, by [Tim Young](https://thenewstack.io/author/tim-young/ "Posts by Tim Young") + +[Will AI force code to evolve or make it extinct?](https://thenewstack.io/ai-programming-languages-future/) + +Mar 22nd 2026 6:00am, by [David Cassel](https://thenewstack.io/author/destiny/ "Posts by David Cassel") + +[Backend Development in 2026: What's Changed, What Matters, and What to Learn Next](https://thenewstack.io/introduction-to-backend-development/) + +Mar 19th 2026 11:37am, by [TNS Staff](https://thenewstack.io/author/tns-staff/ "Posts by TNS Staff") + +[Jellyfish AI development study: The real sting has yet to land](https://thenewstack.io/jellyfish-ai-engineering-trends/) + +Mar 19th 2026 11:01am, by [Adrian Bridgwater](https://thenewstack.io/author/adrian-bridgwater/ "Posts by Adrian Bridgwater") + +[Sampling: the philosopher's stone of distributed tracing](https://thenewstack.io/distributed-tracing-sampling-opentelemetry/) + +Mar 19th 2026 8:00am, by [Michele Mancioppi](https://thenewstack.io/author/michele-mancioppi/ "Posts by Michele Mancioppi") + +[WebAssembly could solve AI agents' most dangerous security gap](https://thenewstack.io/webassembly-sandboxing-ai-agents/) + +Mar 24th 2026 9:01am, by [B. Cameron Gain](https://thenewstack.io/author/bruce-gain/ "Posts by B. Cameron Gain") + +[How WebAssembly plugins simplify Kubernetes extensibility](https://thenewstack.io/how-webassembly-plugins-simplify-kubernetes-extensibility/) + +Mar 3rd 2026 2:00pm, by [B. Cameron Gain](https://thenewstack.io/author/bruce-gain/ "Posts by B. Cameron Gain") + +[WebAssembly is everywhere. Here's how it works](https://thenewstack.io/webassembly-is-everywhere-heres-how-it-works/) + +Feb 25th 2026 11:00am, by [Jessica Wachtel](https://thenewstack.io/author/jessica-wachtel/ "Posts by Jessica Wachtel") + +[Wasm vs. JavaScript: Who wins at a million rows?](https://thenewstack.io/wasm-vs-javascript-who-wins-at-a-million-rows/) + +Feb 22nd 2026 6:00am, by [Jessica Wachtel](https://thenewstack.io/author/jessica-wachtel/ "Posts by Jessica Wachtel") + +[How WebAssembly and Web Workers prevent UI freezes](https://thenewstack.io/how-webassembly-and-web-workers-prevent-ui-freezes/) + +Feb 7th 2026 9:00am, by [Jessica Wachtel](https://thenewstack.io/author/jessica-wachtel/ "Posts by Jessica Wachtel") + +[AI Operations](https://thenewstack.io/ai-operations/) [CI/CD](https://thenewstack.io/ci-cd/) [Cloud Services](https://thenewstack.io/cloud-services/) [DevOps](https://thenewstack.io/devops/) [Kubernetes](https://thenewstack.io/kubernetes/) [Observability](https://thenewstack.io/observability/) [Operations](https://thenewstack.io/operations/) [Platform Engineering](https://thenewstack.io/platform-engineering/) + +[HPE's AI agents cut root cause analysis time in half](https://thenewstack.io/hpes-ai-agents-cut-root-cause-analysis-time-in-half/) + +Mar 25th 2026 7:14am, by +Jennifer Riggins + +[Why most AI projects fail after the demo actually works](https://thenewstack.io/ai-demo-to-production/) + +Mar 25th 2026 4:00am, by +Oladimeji Sowole + +[From pillars to platform: How open observability data is changing the industry](https://thenewstack.io/open-observability-ai-platforms/) + +Mar 20th 2026 6:00am, by +Ted Young + +[Building a Kubernetes-native pattern for AI infrastructure at scale](https://thenewstack.io/kubernetes-native-ai-infrastructure/) + +Mar 19th 2026 5:00am, by +Sachi Desai + +[The AI blind spot debt: the hidden cost killing your innovation strategy](https://thenewstack.io/ai-blind-spot-debt/) + +Mar 17th 2026 7:00am, by +Yuval Fernbach + +[Enterprise dev teams are about to hit a wall. And CI pipelines can't save them.](https://thenewstack.io/ai-agent-validation-bottleneck/) + +Mar 26th 2026 7:00am, by +Anirudh Ramanathan + +[This simple infrastructure gap is holding back AI productivity](https://thenewstack.io/this-simple-infrastructure-gap-is-holding-back-ai-productivity/) + +Feb 22nd 2026 8:00am, by +Charlotte Fleming + +[Ramp’s Inspect shows closed-loop AI agents are software’s future](https://thenewstack.io/ramps-inspect-shows-closed-loop-ai-agents-are-softwares-future/) + +Jan 29th 2026 11:00am, by +Arjun Iyer + +[QCon chat: Is agentic AI killing continuous integration?](https://thenewstack.io/qcon-chat-is-agentic-ai-killing-continuous-integration/) + +Jan 27th 2026 6:00am, by +Joab Jackson + +[Async Rust: Pinning demystified](https://thenewstack.io/async-rust-pinning-demystified/) + +Jan 26th 2026 11:00am, by +Anshul Gupta + +[A practical guide to the 6 categories of AI cloud infrastructure in 2026](https://thenewstack.io/ai-cloud-taxonomy-2026/) + +Mar 15th 2026 5:00am, by +Janakiram MSV + +[Runpod report: Qwen has overtaken Meta's Llama as the most-deployed self-hosted LLM](https://thenewstack.io/runpod-ai-infrastructure-reality/) + +Mar 12th 2026 6:00am, by +Adrian Bridgwater + +[Snowflake Cortex Code CLI adds dbt and Apache Airflow support for AI-powered data pipelines](https://thenewstack.io/snowflake-cortex-code-dbt-airflow/) + +Mar 8th 2026 6:00am, by +Jelani Harper + +[Databases weren’t built for agent sprawl – SurrealDB wants to fix it](https://thenewstack.io/surrealdb-3-ai-agents/) + +Feb 24th 2026 2:07pm, by +Paul Sawers + +[Rising identity complexity: How CISOs can prevent it from becoming an attacker’s roadmap](https://thenewstack.io/ciso-identity-complexity-strategy/) + +Feb 19th 2026 12:47pm, by +Jay Reddy + +[One developer, team power: The future of AI-driven DevSecOps](https://thenewstack.io/future-ai-driven-devsecops/) + +Mar 5th 2026 2:29pm, by +Bryan Ross + +[Observability platform migration guide: Prometheus, OpenTelemetry, and Fluent Bit](https://thenewstack.io/observability-platform-migration-guide/) + +Feb 26th 2026 7:28am, by +Katie Greenley + +[Most platform teams build products, but they don’t know it](https://thenewstack.io/internal-platforms-are-products/) + +Feb 24th 2026 9:00am, by +Oleg Danilyuk + +[Why "automated" infrastructure might cost more than you think](https://thenewstack.io/automated-infrastructure-hidden-costs/) + +Feb 24th 2026 4:00am, by +Justyn Roberts + +[The essential shift every ITOps leader must make to survive an unrelenting stream of incidents](https://thenewstack.io/modernizing-itops-incident-management/) + +Feb 19th 2026 1:46pm, by +Ariel Russo + +[Your Kubernetes isn't ready for AI workloads, and drift is the reason](https://thenewstack.io/ai-workloads-kubernetes-infrastructure-drift/) + +Mar 25th 2026 8:43am, by +TNS Staff + +[Broadcom donates Velero to CNCF — and it could reshape how Kubernetes users handle backup and disaster recovery](https://thenewstack.io/broadcom-velero-cncf-kubernetes/) + +Mar 24th 2026 8:38am, by +B. Cameron Gain + +[IBM, Red Hat, and Google just donated a Kubernetes blueprint for LLM inference to the CNCF](https://thenewstack.io/llm-d-cncf-kubernetes-inference/) + +Mar 24th 2026 8:20am, by +Steven J. Vaughan-Nichols + +[Why WebAssembly won't replace Kubernetes but makes Helm more secure](https://thenewstack.io/helm-webassembly-kubernetes-security/) + +Mar 21st 2026 8:45am, by +B. Cameron Gain + +[Why the ‘glorified host’ for AI is exactly the Kubernetes we need](https://thenewstack.io/kubernetes-glorified-ai-host/) + +Mar 20th 2026 9:00am, by +Danielle Cook + +[From pillars to platform: How open observability data is changing the industry](https://thenewstack.io/open-observability-ai-platforms/) + +Mar 20th 2026 6:00am, by +Ted Young + +[Sampling: the philosopher's stone of distributed tracing](https://thenewstack.io/distributed-tracing-sampling-opentelemetry/) + +Mar 19th 2026 8:00am, by +Michele Mancioppi + +[Why your observability bill keeps growing (and it's not your vendor's fault)](https://thenewstack.io/why-observability-bills-grow/) + +Mar 18th 2026 4:00am, by +Juraci Paixão Kröhling + +[Why agentic AI stalls in production — and how a control plane fixes it](https://thenewstack.io/agentic-ai-control-plane-production/) + +Mar 17th 2026 6:00am, by +TNS Staff + +[Why AI workloads are breaking traditional Kubernetes observability strategies](https://thenewstack.io/ai-kubernetes-observability-practices/) + +Mar 16th 2026 7:04am, by +TNS Staff + +[The operational gap is real, and it's getting wider](https://thenewstack.io/closing-cloud-operational-gap/) + +Mar 26th 2026 8:00am, by +Yevgeny Pats + +[HPE's AI agents cut root cause analysis time in half](https://thenewstack.io/hpes-ai-agents-cut-root-cause-analysis-time-in-half/) + +Mar 25th 2026 7:14am, by +Jennifer Riggins + +[WebAssembly could solve AI agents' most dangerous security gap](https://thenewstack.io/webassembly-sandboxing-ai-agents/) + +Mar 24th 2026 9:01am, by +B. Cameron Gain + +[Kubernetes co-founder Brendan Burns: AI-generated code will become as invisible as assembly](https://thenewstack.io/ai-generated-code-invisible/) + +Mar 24th 2026 7:20am, by +Frederic Lardinois + +[Exton Linux's light version will "enlighten" you](https://thenewstack.io/exton-linuxs-light-version-will-enlighten-you/) + +Mar 24th 2026 5:43am, by +Jack Wallen + +[The operational gap is real, and it's getting wider](https://thenewstack.io/closing-cloud-operational-gap/) + +Mar 26th 2026 8:00am, by +Yevgeny Pats + +[Enterprise dev teams are about to hit a wall. And CI pipelines can't save them.](https://thenewstack.io/ai-agent-validation-bottleneck/) + +Mar 26th 2026 7:00am, by +Anirudh Ramanathan + +[Your Kubernetes isn't ready for AI workloads, and drift is the reason](https://thenewstack.io/ai-workloads-kubernetes-infrastructure-drift/) + +Mar 25th 2026 8:43am, by +TNS Staff + +[Capital One deprecated an AI tool it once championed. Its DevEx chief says that's the point.](https://thenewstack.io/capital-one-developer-enablement/) + +Mar 18th 2026 8:02am, by +Jennifer Riggins + +[From monolith to global mesh: How Uber standardized ML at scale](https://thenewstack.io/uber-standardized-ml-scale/) + +Mar 17th 2026 4:00am, by +Eric Wang and Ying Zheng + +[C++](https://thenewstack.io/c/) [Developer tools](https://thenewstack.io/developer-tools/) [Go](https://thenewstack.io/go/) [Java](https://thenewstack.io/java/) [JavaScript](https://thenewstack.io/javascript/) [Programming Languages](https://thenewstack.io/programming-languages/) [Python](https://thenewstack.io/python/) [Rust](https://thenewstack.io/rust/) [TypeScript](https://thenewstack.io/typescript/) + +[Open source USearch library jumpstarts ScyllaDB vector search](https://thenewstack.io/open-source-usearch-library-jumpstarts-scylladb-vector-search/) + +Feb 5th 2026 12:00pm, by +Jelani Harper + +[AWS WAF vs. Google Cloud Armor: A Multicloud Security Showdown](https://thenewstack.io/aws-waf-vs-google-cloud-armor-a-multicloud-security-showdown/) + +Nov 25th 2025 10:00am, by +Advait Patel + +[Goodbye Dashboards: Agents Deliver Answers, Not Just Reports](https://thenewstack.io/goodbye-dashboards-agents-deliver-answers-not-just-reports/) + +Nov 23rd 2025 9:00am, by +Ketan Karkhanis + +[Rust vs. C++: a Modern Take on Performance and Safety](https://thenewstack.io/rust-vs-c-a-modern-take-on-performance-and-safety/) + +Oct 22nd 2025 2:00pm, by +Zziwa Raymond Ian + +[Building a Real-Time System Monitor in Rust Terminal](https://thenewstack.io/building-a-real-time-system-monitor-in-rust-terminal/) + +Oct 15th 2025 7:05am, by +Tinega Onchari + +[OpenAI acquires Astral to bring open source Python developer tools to Codex — but details are still fuzzy](https://thenewstack.io/openai-astral-acquisition/) + +Mar 20th 2026 7:33am, by +Meredith Shubel + +[Jellyfish AI development study: The real sting has yet to land](https://thenewstack.io/jellyfish-ai-engineering-trends/) + +Mar 19th 2026 11:01am, by +Adrian Bridgwater + +[Sauce Labs wants to solve an AI-created problem nobody wanted to work on](https://thenewstack.io/ai-test-authoring-launch/) + +Mar 18th 2026 9:19am, by +Frederic Lardinois + +[Capital One deprecated an AI tool it once championed. Its DevEx chief says that's the point.](https://thenewstack.io/capital-one-developer-enablement/) + +Mar 18th 2026 8:02am, by +Jennifer Riggins + +[Cursor built a fleet of security agents to solve a familiar frustration](https://thenewstack.io/cursor-open-sources-security-agents/) + +Mar 16th 2026 11:17am, by +Frederic Lardinois + +[Go Experts: 'I Don't Want to Maintain AI-Generated Code'](https://thenewstack.io/go-experts-i-dont-want-to-maintain-ai-generated-code/) + +Sep 28th 2025 6:00am, by +David Cassel + +[How To Run Kubernetes Commands in Go: Steps and Best Practices](https://thenewstack.io/how-to-run-kubernetes-commands-in-go-steps-and-best-practices/) + +Jun 27th 2025 8:00am, by +Sunny Yadav + +[Prepare Your Mac for Go Development](https://thenewstack.io/prepare-your-mac-for-go-development/) + +Apr 12th 2025 7:00am, by +Damon M. Garn + +[Pagoda: A Web Development Starter Kit for Go Programmers](https://thenewstack.io/pagoda-a-web-development-starter-kit-for-go-programmers/) + +Mar 19th 2025 6:10am, by +Loraine Lawson + +[Microsoft TypeScript Devs Explain Why They Chose Go Over Rust, C#](https://thenewstack.io/microsoft-typescript-devs-explain-why-they-chose-go-over-rust-c/) + +Mar 18th 2025 7:00am, by +David Cassel + +[Java 26 lands without an LTS badge. Here's why developers should care anyway.](https://thenewstack.io/java-26-performance-ai/) + +Mar 18th 2026 9:35am, by +Darryl K. Taft + +[62% of enterprises now use Java to power AI apps](https://thenewstack.io/2026-java-ai-apps/) + +Feb 10th 2026 12:58pm, by +Darryl K. Taft + +[BellSoft bets Java expertise can beat hardened container wave](https://thenewstack.io/bellsoft-bets-java-expertise-can-beat-hardened-container-wave/) + +Jan 26th 2026 3:00pm, by +Darryl K. Taft + +[Java Developers Get Multiple Paths To Building AI Agents](https://thenewstack.io/java-developers-get-multiple-paths-to-building-ai-agents/) + +Dec 26th 2025 7:02am, by +Darryl K. Taft + +[Your Enterprise AI Strategy Must Start With Java, Not Python](https://thenewstack.io/your-enterprise-ai-strategy-must-start-with-java-not-python/) + +Dec 22nd 2025 1:00pm, by +Michael Coté + +[TypeScript 6.0 RC arrives as a bridge to a faster future](https://thenewstack.io/typescript-6-0-rc-arrives-as-a-bridge-to-a-faster-future/) + +Mar 14th 2026 9:00am, by +Darryl K. Taft + +[WebAssembly is everywhere. Here's how it works](https://thenewstack.io/webassembly-is-everywhere-heres-how-it-works/) + +Feb 25th 2026 11:00am, by +Jessica Wachtel + +[Wasm vs. JavaScript: Who wins at a million rows?](https://thenewstack.io/wasm-vs-javascript-who-wins-at-a-million-rows/) + +Feb 22nd 2026 6:00am, by +Jessica Wachtel + +[Arcjet reaches v1.0, promises stable security for JavaScript apps](https://thenewstack.io/arcjet-reaches-v1-0-promises-stable-security-for-javascript-apps/) + +Feb 14th 2026 7:00am, by +Darryl K. Taft + +[How WebAssembly and Web Workers prevent UI freezes](https://thenewstack.io/how-webassembly-and-web-workers-prevent-ui-freezes/) + +Feb 7th 2026 9:00am, by +Jessica Wachtel + +[Will AI force code to evolve or make it extinct?](https://thenewstack.io/ai-programming-languages-future/) + +Mar 22nd 2026 6:00am, by +David Cassel + +[Java 26 lands without an LTS badge. Here's why developers should care anyway.](https://thenewstack.io/java-26-performance-ai/) + +Mar 18th 2026 9:35am, by +Darryl K. Taft + +[TypeScript 6.0 RC arrives as a bridge to a faster future](https://thenewstack.io/typescript-6-0-rc-arrives-as-a-bridge-to-a-faster-future/) + +Mar 14th 2026 9:00am, by +Darryl K. Taft + +[Nearly half of all companies now use Rust in production, survey finds](https://thenewstack.io/rust-enterprise-developers/) + +Mar 6th 2026 10:45am, by +Darryl K. Taft + +[Statistical language R is making a comeback against Python](https://thenewstack.io/statistical-language-r-is-making-a-comeback-against-python/) + +Feb 12th 2026 2:57pm, by +Darryl K. Taft + +[OpenAI acquires Astral to bring open source Python developer tools to Codex — but details are still fuzzy](https://thenewstack.io/openai-astral-acquisition/) + +Mar 20th 2026 7:33am, by +Meredith Shubel + +[Python virtual environments: isolation without the chaos](https://thenewstack.io/python-virtual-environments-isolation-without-the-chaos/) + +Feb 16th 2026 7:00am, by +Jessica Wachtel + +[Statistical language R is making a comeback against Python](https://thenewstack.io/statistical-language-r-is-making-a-comeback-against-python/) + +Feb 12th 2026 2:57pm, by +Darryl K. Taft + +[Arcjet's Python SDK Embeds Security in Code](https://thenewstack.io/arcjets-python-sdk-embeds-security-in-code/) + +Jan 16th 2026 2:00pm, by +Darryl K. Taft + +[2025: The Year of the Return of the Ada Programming Language?](https://thenewstack.io/2025-the-year-of-the-return-of-the-ada-programming-language/) + +Jan 14th 2026 4:00pm, by +Darryl K. Taft + +[Nearly half of all companies now use Rust in production, survey finds](https://thenewstack.io/rust-enterprise-developers/) + +Mar 6th 2026 10:45am, by +Darryl K. Taft + +[Wasm vs. JavaScript: Who wins at a million rows?](https://thenewstack.io/wasm-vs-javascript-who-wins-at-a-million-rows/) + +Feb 22nd 2026 6:00am, by +Jessica Wachtel + +[Open source USearch library jumpstarts ScyllaDB vector search](https://thenewstack.io/open-source-usearch-library-jumpstarts-scylladb-vector-search/) + +Feb 5th 2026 12:00pm, by +Jelani Harper + +[The 'weird' things that happened when Clickhouse replaced C++ with Rust](https://thenewstack.io/the-weird-things-that-happened-when-clickhouse-replaced-c-with-rust/) + +Feb 4th 2026 7:26am, by +B. Cameron Gain + +[Async Rust: Pinning demystified](https://thenewstack.io/async-rust-pinning-demystified/) + +Jan 26th 2026 11:00am, by +Anshul Gupta + +[TypeScript 6.0 RC arrives as a bridge to a faster future](https://thenewstack.io/typescript-6-0-rc-arrives-as-a-bridge-to-a-faster-future/) + +Mar 14th 2026 9:00am, by +Darryl K. Taft + +[Mastra empowers web devs to build AI agents in TypeScript](https://thenewstack.io/mastra-empowers-web-devs-to-build-ai-agents-in-typescript/) + +Jan 28th 2026 11:00am, by +Loraine Lawson + +[Inferno Vet Creates Frontend Framework Built With AI in Mind](https://thenewstack.io/inferno-vet-creates-frontend-framework-built-with-ai-in-mind/) + +Dec 10th 2025 11:00am, by +Loraine Lawson + +[JavaScript Utility Library Lodash Changing Governance Model](https://thenewstack.io/javascript-utility-library-lodash-changing-governance-model/) + +Nov 1st 2025 7:00am, by +Loraine Lawson + +[Microsoft TypeScript Devs Explain Why They Chose Go Over Rust, C#](https://thenewstack.io/microsoft-typescript-devs-explain-why-they-chose-go-over-rust-c/) + +Mar 18th 2025 7:00am, by +David Cassel + +2025-11-04 05:00:01 + +It’s Time To Build APIs for AI, Not Just For Developers + +contributed, + +[AI Agents](https://thenewstack.io/category/ai-agents/) / [AI Engineering](https://thenewstack.io/category/ai-engineering/) + +# It’s Time To Build APIs for AI, Not Just For Developers + +If your APIs were built for humans, they're probably failing AI agents. It's time for a paradigm shift to AI-first design. + +Nov 4th, 2025 5:00am by [Saqib Jan](https://thenewstack.io/author/saqib-jan/ "Posts by Saqib Jan") + +![Featued image for: It’s Time To Build APIs for AI, Not Just For Developers](https://cdn.thenewstack.io/media/2025/10/35c6b952-agent-1024x576.jpg) + +Image from Thapana\_Studio on Shutterstock. + + +For the last two decades, the [principles of API design](https://thenewstack.io/what-are-the-core-principles-of-good-api-design/) have centered around the human developer. We built systems optimized for their convenience, with flexible endpoints and rich [documentation](https://thenewstack.io/bad-documentation-bad-documentation/) that they could interpret. + +But a new and powerful class of consumer is already disrupting in the form of autonomous [AI agents](https://thenewstack.io/ai-agents-a-comprehensive-introduction-for-developers/) that operate on a fundamentally different set of principles, which require a new approach to the way we build and describe our services. + +“This means a new paradigm for us as developers: We must now build APIs optimized for consumption by machines, which requires a fundamentally different design philosophy than the one we use for human-centric development,” says [Srinivasan Sekar](https://in.linkedin.com/in/srinivasan-sekar), a director of engineering at [LambdaTest](https://www.lambdatest.com/), an AI-native software testing platform. + +This increasing shift toward an “ [AI-first](https://thenewstack.io/ai-first-web-development-model-first-design-and-remix-v3/)” design philosophy prioritizes the explicit clarity and predictability that machines require to reason and act effectively. It underpins a clear framework for redesigning systems for the new agentic world. + +## Shift From ‘Developer-First’ to ‘AI-First’ + +At the heart of this new manifesto is a core cultural shift that must precede any architectural changes: moving from a “developer-first” to an [“AI-first” design](https://thenewstack.io/why-api-first-matters-in-an-ai-driven-world/). As Sekar explains, for years, we have optimized our APIs for the convenience of human developers. + +This approach favors flexibility, often resulting in fewer, multipurpose endpoints and a reliance on external documentation to clarify ambiguity. A human developer can read a guide to understand that a certain parameter is required only when another is present — a nuance we have long taken for granted. + +> “We must now build APIs optimized for consumption by machines, which requires a fundamentally different design philosophy than the one we use for human-centric development.” +> +> **— Srinivasan Sekar, director of engineering at LambdaTest** + +AI agents, however, are fundamentally different consumers. They cannot read external documentation or infer implicit relationships between parameters. An AI operates solely on the explicit, machine-readable contract provided by the API’s schema. This, he argues, is the crux of the “AI-first” philosophy: a design approach that prioritizes the absolute, unambiguous clarity that machines require, leaving no room for interpretation in the contract. + +TRENDING STORIES + +1. [Four prompt engineering patterns every developer should know — and why "draw a cat" explains them all](https://thenewstack.io/prompt-engineering-for-developers/) +2. [Why most AI projects fail after the demo actually works](https://thenewstack.io/ai-demo-to-production/) +3. [Anthropic: You can still use your Claude accounts to run OpenClaw, NanoClaw and Co.](https://thenewstack.io/anthropic-agent-sdk-confusion/) +4. [Capital One deprecated an AI tool it once championed. Its DevEx chief says that's the point.](https://thenewstack.io/capital-one-developer-enablement/) +5. [Perplexity Computer wows, Karpathy kills vibe coding, and OpenAI replaces Anthropic at the Pentagon](https://thenewstack.io/perplexity-computer-vibe-coding-openai-anthropic-pentagon/) + +[Sai Krishna](https://www.linkedin.com/in/sai-krishna-3755407b/), working alongside Sekar, adds a practical dimension to this shift: “At LambdaTest, we learned this the hard way. We had a perfectly functional API for configuring test environments that developers loved for its flexibility. But when AI agents started using it, we saw a 40% failure rate because the agents couldn’t interpret the implicit rules we’d documented separately. We had to completely rethink our approach.” + +This means favoring more specific, single-purpose endpoints and defining all constraints explicitly within the schema itself. This mindset is the non-negotiable foundation for [building any successful and reliable agentic system](https://thenewstack.io/agentic-ai-tools-for-building-and-managing-agentic-systems/). + +## Unlearning the Three Habits of Human-Centric APIs + +Adopting this “AI-first” philosophy in practice means actively unlearning several ingrained habits of traditional, human-centric API design. Sekar identifies three common patterns that, while convenient for human developers, create [critical failures when consumed by AI agents](https://thenewstack.io/no-apis-no-ai-why-api-access-is-critical-to-agentic-systems/). + +First is the habit of overloading single endpoints with multiple behaviors. A developer can handle this flexibility, but an AI agent struggles with the ambiguity. The AI-first approach requires distinct, single-purpose endpoints where the function is explicit. + +- **Before:** A single `POST /user` endpoint would ambiguously handle both creating and updating a user based on whether an `id` was present in the payload. +- **After:** The AI-first approach uses two distinct and predictable endpoints: `POST /users` to create a new user and `PUT /users/{id}` to update an existing one. + +Second is the reliance on implicit contracts and external documentation. For an agent to act reliably, all parameter relationships and dependencies must be explicitly declared within the machine-readable schema itself. + +- **Before:** A traditional schema would list `user_type` and `admin_level` as optional, forcing a developer to read external documentation to learn their conditional relationship. +- **After:** An AI-first schema makes this relationship explicit using conditional logic, allowing a machine to understand the contract without any external context. + +Finally, teams must unlearn the habit of providing generic error responses. An AI-first API must provide structured, detailed error responses that allow an agent to self-correct. + +- **Before:** A generic JSON response like `{"message": "Bad Request"}` would halt an automated workflow. +- **After:** A structured JSON error provides specific fields for the error `code`, `message` and `details`, indicating exactly which parameter was invalid. + +These shifts share the common purpose of eliminating ambiguity. By making endpoints, contracts and errors explicit, developers provide the predictable foundation necessary for autonomous agents to act reliably and effectively. + +## The New Pillars of AI-First Design + +Beyond simply avoiding old habits, building an AI-first API requires embracing a new set of positive design principles centered on clarity and predictability. + +“This begins with semantic clarity,” says [Sean Falconer,](https://www.linkedin.com/in/seanf/) senior director of product, AI strategy at [Confluent](https://www.confluent.io/). A truly AI-native API must do more than just describe its technical function; its machine-readable contract must also describe its business purpose, its prerequisites and any potential side effects. This provides the rich context an [AI agent needs to reason](https://thenewstack.io/4-reasons-agentic-ai-is-failing/) about not just how to use a tool, but when and why. + +> A truly AI-native API must do more than just describe its technical function; its machine-readable contract must also describe its business purpose, its prerequisites and any potential side effects. + +This means developers must enrich their API schemas, moving beyond simple data types. For example, in an OpenAPI specification, every parameter and endpoint should include a detailed description that explains not just the “what” (for instance, an integer ID), but the “why” (such as the unique customer identifier used for billing and support tickets). + +This level of clarity is best achieved by designing what Falconer refers to as small, purpose-built tools rather than exposing large, generic API surfaces. [Yoni Michael](https://www.linkedin.com/in/yonimichael), CTO of [Typedef](https://www.typedef.ai/), agrees with this principle, advocating for a “minimal surface area,” meaning the API should expose only what is absolutely essential for a given task. + +For architects, this translates into a clear design mandate: Resist the urge to create monolithic, all-purpose endpoints. Instead, complex business processes should be broken down into their smallest logical components, with a dedicated, constrained API designed for each one. A sprawling `/orders` API, for instance, could be refactored into focused, purpose-built tools like `/create-order`, `/check-order-status` and `/request-refund`. Creating these well-defined tools reduces ambiguity and the cognitive load on the AI, making its behavior easier to govern and evaluate. + +All of these principles serve a single, critical goal: achieving what Michael calls deterministic behavior. An autonomous agent cannot afford surprises when it is chaining together multiple tools to execute a complex workflow. The system must be utterly reliable and predictable. + +To deliver on this, engineers must prioritize rigorous testing and stateless design where possible. Every API call with the same inputs should consistently produce the same output, free from hidden dependencies or unpredictable side effects. This involves providing clear, idempotent interfaces for any operations that modify data, ensuring that repeated calls do not have unintended consequences. + +By building APIs with semantic clarity, a minimal surface area and a clear purpose, architects provide the foundation of trust that allows an AI agent to build upon them effectively. + +## Hurdles of Reshaping Data for AI + +Even with these forward-thinking design principles in place, there is a final, deeper architectural challenge that underpins all AI-first design. Sekar from LambdaTest identifies this as the most significant hurdle of all: the difficult but necessary task of data model flattening. + +He explains that most existing enterprise APIs reflect deep-rooted data platform challenges, as they were designed around complex internal database schemas or nested object models. While a human developer can navigate these intricate structures, they create significant “cognitive overhead” for an AI agent. + +A deeply nested data structure forces an AI model to [expend valuable resources simply understanding](https://thenewstack.io/automating-context-in-structured-data-for-llms/) the shape of the data and the relationships between its parts before it can even begin to act on the information. + +This complexity introduces a high potential for error and makes the agent’s behavior less predictable. The AI-first solution is to flatten and normalize these data models, redesigning them into simpler, more predictable formats that are optimized for machine consumption. + +> “The companies building the most reliable agentic systems aren’t necessarily the ones with the most sophisticated AI models. They’re the ones who’ve done the hard work of redesigning their API foundations to speak the language machines understand.” +> +> **— Srinivasan Sekar** + +And this is usually the most resource-intensive part of the journey to becoming AI native. Sekar argues that this task goes far beyond simply documenting existing systems. It frequently requires a fundamental redesign of the data access layer and the creation of entirely new, parallel API surfaces that are purpose-built for AI agents. + +Krishna shares the practical reality of this transformation: “We maintain two API layers now; our legacy developer API and our AI-optimized API. The AI version takes a test result object that was previously nested four levels deep and flattens it into a single-level structure with explicit relationship IDs. It tripled our schema size but cut agent processing time by 70%. The investment was significant, but necessary.” This ensures that the context provided to the AI is not just semantically clear, but also structurally simple and immediately useful. + +## The Future Is a ‘Behavioral Contract’ + +Taken together, these principles — a cultural shift to an AI-first mindset, the unlearning of old habits and a deep architectural commitment to clarity and simple data models — form a new manifesto for API design. But the impact of this new philosophy extends beyond the initial engineering of our systems and into their entire life cycle. + +Sekar predicts that this will ultimately reshape core DevOps practices like API versioning. In a world where autonomous agents are the primary consumers, the focus of API management will shift from tracking simple syntax changes to guaranteeing “behavioral contracts.” The promise to the AI will no longer be just that the API’s structure is stable, but that its behavior is consistent and predictable, ensuring the same inputs always produce the expected type of outcome. + +Krishna elaborates on how this plays out operationally: “We’ve started versioning our behavioral contracts separately from our API versions. An agent subscribes to a behavioral contract, say, ‘search capability with pagination,’ and we guarantee that contract’s behavior even as we evolve the underlying implementation. If we need to change behavior, we introduce a new contract version, giving agents time to adapt.” + +Both Sekar and Krishna emphasize that this commitment to explicit, predictable and behaviorally consistent APIs is the ultimate expression of the AI-first philosophy. + +“The companies building the most reliable agentic systems aren’t necessarily the ones with the most sophisticated AI models,” Sekar notes. “They’re the ones who’ve done the hard work of redesigning their API foundations to speak the language machines understand.” + +This foundation is what the next generation of reliable agentic AI applications will be built upon. + +[YOUTUBE.COM/THENEWSTACK\\ +\\ +\\ +Tech moves fast, don't miss an episode. Subscribe to our YouTube \\ +channel to stream all our podcasts, interviews, demos, and more.\\ +\\ +\\ +SUBSCRIBE](https://youtube.com/thenewstack?sub_confirmation=1) + +GroupCreated with Sketch. + +[![](https://thenewstack.io/wp-content/uploads/2025/10/85eac203-cropped-f9e1bb76-screenshot-2025-10-27-at-2.36.21%E2%80%AFpm-600x600.png)\\ +\\ +Saqib Jan is a technology analyst with experience in application development, FinOps and cloud technologies. \\ +\\ +Read more from Saqib Jan](https://thenewstack.io/author/saqib-jan/) + +SHARE THIS STORY + +SHARE THIS STORY + +TNS DAILY NEWSLETTER +Receive a free roundup of the most recent TNS articles in your inbox each day. + +SUBSCRIBE + + +The New Stack does not sell your information or share it with +unaffiliated third parties. By continuing, you agree to our +[Terms of Use](https://thenewstack.io/terms-of-use/) and +[Privacy Policy](https://thenewstack.io/privacy-policy/). \ No newline at end of file diff --git a/.firecrawl/beyond-bespoke-ui.md b/.firecrawl/beyond-bespoke-ui.md new file mode 100644 index 0000000..9ae99a9 --- /dev/null +++ b/.firecrawl/beyond-bespoke-ui.md @@ -0,0 +1,455 @@ +Authors + +- ![avatar](https://www.cress.codes/_next/image?url=%2Fstatic%2Fimages%2Fkale_jungle.jpg&w=96&q=75)NameDaniel CressTwitter + +# Beyond Bespoke: How AI Turns Component Libraries Into Adaptive Systems + +Every time we build a new feature, we follow the same ritual: design mockups, build custom components, wire up state management, test variations, deploy. Then the requirements change slightly, and we do it all again. A dashboard for managers needs different cards than one for employees. A mobile form needs different layouts than desktop. A beginner's view needs simpler options than an expert's. + +We've gotten really good at building bespoke experiences. Maybe too good. We've optimized the process of creating unique interfaces for every context, every user type, every edge case. But what if we're solving the wrong problem? + +What if instead of building better tools for creating variations, we built systems that generate variations on demand? What if our component libraries could adapt themselves based on context, using the same primitives we already have? + +This isn't about replacing Nuxt UI or Shadcn or Radix. It's about teaching them to compose themselves. + +## What We Built at Bambee + +At Bambee, we built a system that generates contextual UI based on vast amounts of workplace data—performance metrics, compliance requirements, employee sentiment, turnover patterns, organizational health indicators. The challenge wasn't just showing data; it was surfacing the right insights with the right actions at the right time. + +A manager dealing with high turnover needs different recommendations than one managing a stable team. An employee in their first month needs different guidance than a three-year veteran. A compliance alert for California employment law requires different visualizations than one for federal OSHA requirements. + +We started down the familiar path: custom components for each scenario. But the combinatorial explosion quickly became clear. Dozens of card types. Countless variations of forms. Complex conditional logic everywhere. + +So we tried something different. + +### Dynamic Cards and Notices + +The system analyzes company data and generates recommendations—we call them solutions and notices. Each one has different severity levels, different actions users can take, different visualizations to make the data clear. + +One user might see a compliance alert with a bar chart showing policy gaps across departments. Another sees a performance insight with a timeline visualization of team productivity trends. A third sees a recognition opportunity with a simple progress indicator. + +Here's the key: we're not building separate components for each type. We're using the same card component, the same underlying UI primitives from our component library. What changes is the structured data that drives them. + +The system generates a schema-compliant payload that describes what to show, how to show it, and what actions should be available. The frontend trusts that structure and renders accordingly. + +### Dynamic Wizards + +The second use case was even more interesting: multi-step wizards that adapt to context. + +The system detects information gaps—missing data that would improve recommendations. Based on what's missing and how critical it is, it generates a wizard to collect that information. + +Sometimes it's a simple two-step survey: "What's your management philosophy? How large is your team?" Other times it's a comprehensive five-step wizard with conditional logic: if you answer yes to one question, you see follow-up questions; if you answer no, you skip to the next section. + +The question types change dynamically. Sliders for rating scales. Checkbox groups for multiple selections. Matrices for comparing options across criteria. Date pickers for timelines. All generated from schemas, all rendered by the same form components. + +We're not pre-building every possible wizard variation. We're defining the contract—what a wizard can contain, what question types are valid, how steps can be arranged—and letting the system compose variations on the fly. + +## The Core Principles + +Building this taught us some things about how to structure systems for dynamic UI generation. These aren't prescriptive rules, just patterns that worked for us. + +### Schema as Contract + +In traditional development, you design an interface, build the component, then wire it to data. The component defines what's possible. + +In schema-driven development, you define the contract first. The schema is the source of truth. It describes what shapes of data are valid, what fields are required, what values are acceptable. + +The backend generates data conforming to that schema. The frontend trusts the structure and renders accordingly. Neither side makes assumptions beyond what the schema guarantees. + +This inverts the usual relationship. Instead of data conforming to components, components adapt to data (within the bounds of the schema). + +A simple example: + +```typescript +// Schema defines possibilities +ActionCardSchema = { + type: 'ACTION_CARD', + severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL', + action: { + type: string, + label: string, + handler: { route: string, params: object } + }, + visualization?: { + type: 'BAR' | 'LINE' | 'PIE', + data: object + } +} + +// Backend generates instance based on context +const generatedCard = analyzeContext(userData) + +// Frontend renders using component library primitives + +``` + +The schema is doing a lot of work here. It's defining not just data types, but the vocabulary of what interfaces can express. Add a new visualization type to the schema, teach the frontend to render it, and suddenly all generated cards can use it. + +### Type Safety Through Validation + +The critical enabler for this approach is runtime validation. For us, that means Zod, but the principle applies to any schema validation library. + +Here's why it matters: AI generates JSON. That JSON must match the exact structure your frontend expects. If it doesn't, you get runtime errors, broken UI, frustrated users. + +With runtime validation, you create a feedback loop. AI generates output. You validate it immediately against your schema. If it fails validation, you send the errors back to the AI and ask it to regenerate. + +The pattern looks like: + +1. AI generates structured output based on context +2. Validate against schema immediately +3. If invalid → capture specific validation errors +4. Send those errors back to AI with original context +5. AI regenerates, accounting for what went wrong +6. Retry with exponential backoff (2-3 attempts max) +7. If all retries fail → fallback to safe default + +This creates remarkably reliable output. The AI learns your schema requirements through the error messages. After a few iterations of improving prompts and tightening schemas, validation failures become rare. + +The validation itself is straightforward: + +```typescript +try { + const validated = CardSchema.parse(aiOutput) + return validated +} catch (error) { + await retryWithRefinement(aiOutput, error) +} +``` + +But the implications are profound. Your frontend never sees invalid data. Type safety is enforced at runtime. Breaking changes to schemas are caught immediately, not in production. + +### Slots Over Specifics + +We stopped building ``, ``, ``. Instead, we built `` that accepts structured data and routes to appropriate presentation. + +The slot examines the schema, determines which component primitives to use, and composes the final UI. + +A compliance alert might render as: red badge with alert icon, bar chart visualization, "Review Policy" button that routes to policy creation. A performance insight might render as: blue badge with trend icon, line chart visualization, "View Details" button that opens a details modal. + +Same slot. Same underlying Button, Card, Badge, and Chart components from our component library. Different compositions based on the schema data. + +This is more than just abstraction. It's a different mental model. You're not building components for specific use cases. You're building a rendering engine that interprets schemas and composes UI from primitives. + +The power comes from the mapping layer. It reads the schema and makes decisions: + +- What badge color and icon represent this severity level? +- Which chart component matches this visualization type? +- What button variant and text match this action type? +- How should these elements be arranged for this context? + +As you add more schema types, the mapping layer grows. But the underlying components stay the same. + +### Visualization as Configuration + +Instead of building separate chart components for every context, we treat visualizations as configuration. + +The backend sends structured data describing what to visualize and how: + +```js +{ + visualizationType: 'BAR', + data: { + labels: ['Q1', 'Q2', 'Q3', 'Q4'], + values: [23, 45, 67, 89] + }, + theme: 'minimal', + options: { showLegend: false } +} +``` + +The frontend has a factory function. It looks at the visualization type and says, "BAR chart? Render the BarChart component from our library with these props." + +This means adding a new visualization type is a small change. Add the type to your schema, teach the factory to handle it, and now every part of your system that generates visualizations can use it. + +We extended this with a vector database of illustrations. The system can describe the visual context it needs—"performance improvement scenario with upward trend"—then query embeddings to find the closest matching illustration from our library. No manual asset selection. Just semantic matching between generated context and available visuals. + +### Dynamic Wizards from Schemas + +Multi-step forms become declarative data structures. + +A wizard schema defines: + +- How many steps +- What questions appear in each step +- Question types and validation rules +- Conditional display logic +- Progress indicators and navigation + +The frontend loops through the schema and renders appropriate input components. A question with type `'SLIDER'` renders your library's slider component. A question with type `'CHECKBOX'` renders a checkbox group. + +The power is in the conditional logic. Questions can show or hide based on previous answers. Entire steps can be skipped based on user context. Validation rules can reference other questions. + +All described in the schema. All rendered by generic form components. + +Adding a new question type means updating the schema to include it and teaching one component to render it. Not rebuilding every wizard that might use it. + +This is where it clicked for me: we weren't building forms anymore. We were building a form generation engine. + +## The Future Vision + +This is where it gets interesting. What we built at Bambee is a proof of concept. It works in production, handles real complexity, serves real users. But it's just scratching the surface of what's possible. + +Let me paint some pictures of where this could go. To illustrate these concepts without diving into proprietary specifics, I'll use a hypothetical recipe and meal planning platform as a concrete example—but these patterns apply across any domain with similar variability. + +### Pages That Generate Themselves + +Imagine you're building a recipe and meal planning platform. A user opens the app and says, "I want to plan meals for the week." + +The system analyzes their context: + +- Family size and ages +- Dietary restrictions and preferences +- Available cooking time +- Current pantry inventory +- Cooking skill level +- Budget constraints +- Past meal preferences + +From this, it generates a complete page schema. Not a page that exists in your codebase. A page composed on the fly: + +A three-column dashboard layout. Left column shows a weekly calendar with meal slots, each slot showing prep time and ingredient overlap with other meals. Center column shows a shopping list organized by grocery store section, with cost estimates and substitution suggestions. Right column shows a nutrition summary chart aggregating the week's macros, plus a comparison to their goals. + +At the bottom, an action row with context-aware buttons: export meal plan to calendar, send shopping list to grocery app, adjust for budget, regenerate for more variety. + +The frontend receives this schema and composes it using your component library's Grid, Card, Calendar, List, Chart, and Button components. The page never existed before this moment. It was assembled because this specific user, in this specific context, needed this specific combination of features. + +That's the radical shift: from pages as files in a repository to pages as generated compositions. The repository contains the components and the schemas. The combinations emerge from context. + +### Context-Aware Form Adaptation + +Same underlying data, completely different form based on who's using it. + +A beginner user gets a simplified recipe entry form: basic fields, lots of help text, suggested defaults, links to video tutorials explaining cooking terms. Single-column layout, large touch targets, progress saved after every field. + +An advanced user gets the compact version: all fields visible, technical terminology, advanced options like ingredient ratios and technique variations, keyboard shortcuts for quick entry. Multi-column layout, minimal explanatory text, batch editing capabilities. + +The system decides which variation to show based on user behavior analysis. Not A/B testing. Not user segments. Individual adaptation. + +And it goes deeper. Forms that adapt mid-flow based on answers. User selects "dietary restriction: vegan" → the form immediately hides all questions about meat preparation, adds questions about B12 supplementation, adjusts the nutrition target ranges, suggests vegan protein sources in the ingredient picker. + +The form is responding to context in real-time, reshaping itself to show what's relevant and hide what isn't. + +### Adaptive Complexity + +Interfaces that scale complexity based on user sophistication, not just hiding advanced features behind a settings toggle. + +A recipe platform might show the same dish completely differently based on skill level. Beginners see: "Sauté the onions until soft" with a photo showing what "soft" looks like and a link to a basic sautéing video. Intermediate cooks see: "Sauté onions in butter over medium heat until translucent, 5-7 minutes" with suggested pan types and heat settings. Advanced cooks see: "Sweat onions in clarified butter, 82°C, until cell walls break down but no Maillard reaction occurs—monitor for steam release, not browning" with technique alternatives like using a immersion circulator for precise temperature control. + +The ingredient list adapts too. Beginners see standard grocery store items. Intermediate cooks see preferred brands and substitution options. Advanced users see specific varieties ("Vidalia onions for sweetness, or shallots for depth"), quality indicators, and even molecular composition notes for technique-critical ingredients. + +Same recipe. Same underlying data. But the interface reveals layers of technical sophistication progressively, matching what each user can handle and wants to see. + +The system watches behavior. User consistently completes advanced recipes without issues? Start showing more complexity. User struggles with intermediate recipes? Pull back to basics. + +This isn't just hiding fields or showing tooltips. It's fundamentally different interfaces generated for different capability levels, all from the same underlying schemas. + +### Cross-Domain Schema Standards + +Here's where my mind goes to interesting places. + +What if schema patterns became standardized across domains? + +An `ACTION_CARD` schema could power meal suggestions in a recipe app, workout recommendations in a fitness app, budget alerts in a finance app, treatment plan updates in a healthcare app. Different domains, same structure: context analysis → recommended action → visualization → user choice. + +A `WIZARD` schema could power dietary preference surveys in a recipe app, goal-setting wizards in a fitness app, budget creation flows in a finance app, symptom checkers in a healthcare app. Same multi-step structure, same question types, same conditional logic patterns. + +Your component library becomes a universal renderer for structured intents. You build the primitives once—cards, forms, charts, buttons—and they work across every domain that speaks the schema language. + +This is bigger than code reuse. It's conceptual reuse. The patterns for how to structure adaptive UI become portable knowledge, not locked into specific implementations. + +Imagine open schema standards for common UI patterns. Like how we have standard HTTP methods or standard database query languages, we could have standard schemas for "action recommendation with visualization" or "multi-step data collection with conditional logic." + +Build a great implementation once, use it everywhere. + +### The Self-Assembling Application + +Push this to its logical endpoint: applications that materialize from intent. + +You describe what you want in natural language: "I want to help users plan healthy meals on a budget with easy recipes they can actually make." + +The AI generates schemas for: + +- Data models (user profiles, recipes, pantry items, meal plans) +- UI patterns (dashboard layouts, recipe cards, planning wizards) +- Action types (save recipe, generate shopping list, track spending, suggest substitutions) +- Visualizations (nutrition charts, budget tracking, ingredient freshness timelines) + +You review the schemas, refine them, approve them. The frontend already knows how to render any valid schema. The backend already knows how to validate and store schema-conforming data. + +What you're doing is no longer building features. You're curating and refining schemas. The system does the composition. + +This sounds far-fetched until you realize we're already doing pieces of it. Code generation tools are getting better. Schema validation is mature. Component libraries are comprehensive. AI understands context remarkably well. + +We're just connecting the pieces. + +### Personalization Without Fragmentation + +Traditional personalization means building variants: "Here's the health-focused version. Here's the budget-focused version. Here's the time-saving version." + +You end up managing three codebases pretending to be one. Changes require updating all variants. Testing multiplies. Maintenance becomes painful. + +Schema-driven personalization means generating the optimal interface for each user from the same underlying system. + +User A cares about health. Their recipe cards prominently display nutrition information, macro breakdowns, ingredient quality scores, health impact summaries. The charts show nutrient density. The recommendations prioritize nutritional completeness. + +User B cares about time. Their recipe cards show prep time first, active vs. passive time breakdowns, make-ahead options, batch cooking opportunities. The charts show time saved through meal prep. The recommendations prioritize efficiency. + +User C cares about budget. Their recipe cards show cost per serving, bulk buying opportunities, seasonal ingredient savings, leftover utilization. The charts show cost comparisons. The recommendations prioritize affordability. + +Same data. Same component library. Same schemas. Infinitely variable presentation based on what matters to each individual user. + +No variant management. No A/B test fragments. No "which version am I in?" confusion. Just contextual generation. + +And because it's schema-driven, you can combine dimensions. User D cares about both health and budget. They get nutrition data weighted by cost-effectiveness. User E cares about time and is also vegan. They get quick recipes that happen to be plant-based, not "vegan recipes" as a special category. + +The combinations emerge from the generation logic, not from pre-built variants. + +## The Trade-offs + +This sounds exciting, and it is. But it's not free. The complexity moves around; it doesn't disappear. + +### Schema Management Becomes Critical + +Your schemas are your contract. They're what enables the whole system to work. Which means changing them is like changing your API. + +You need versioning strategies. How do you evolve schemas without breaking existing data? How do you migrate old schema instances to new versions? How do you deprecate schema fields while maintaining backward compatibility? + +You need synchronization mechanisms. The backend that generates schemas and the frontend that renders them must stay aligned. A mismatch means broken UI or validation errors. + +You need discovery tools. As schemas multiply, developers need ways to find them, understand them, know which one to use for which scenario. Documentation becomes more important, not less. + +This is real work. You might need a schema registry, similar to what you'd use for event-driven architectures. You might need automated testing that validates schema compatibility across versions. You might need tooling to generate TypeScript types from schemas and keep them in sync. Tools like Storybook become invaluable—you can document not just components, but how those components render different schema variations. Each story becomes a living example of "here's this schema shape, here's how it renders," making it easier for developers to understand which schema to use for which scenario. + +Schema management is now a first-class concern in your architecture. + +### Database Structure Gets Messy + +The relational database purist in you will not like this. + +Instead of neat columns with proper foreign keys and database-level constraints, you end up with JSONB blobs. The validation that would normally happen at the database layer moves to application code. Querying becomes harder—you can't easily "show me all high-priority compliance actions" when that data is buried in JSON. + +There are workarounds. Hybrid models where you extract commonly-queried fields to columns and keep the dynamic data as JSON. PostgreSQL's JSONB type with indexes on frequently-accessed paths. Generated columns that pull specific values out of JSON for querying. Materialized views for complex queries that need to run often. + +But it's more complex than traditional relational design. You're trading structure for flexibility. + +The database is no longer your source of truth for what data is valid. Your schemas are. The database is just storage. + +This is a philosophical shift as much as a technical one. + +### Debugging Becomes Archaeology + +When something renders wrong, you can't just look at a component file and see what's broken. + +You need to trace: What was the user's context? What did the AI decide based on that context? What schema did it generate? Did it pass validation? How did the mapping layer interpret it? Which component got selected? What props were passed? + +That's a lot of layers between "user saw wrong thing" and "here's the bug." + +You need comprehensive logging at each step. You need replay tools that can take a saved context and schema and show you exactly how it rendered. You need schema versioning so you know which version of which schema generated which UI at which time. You need visibility into AI decision-making—why did it choose this action type over that one? + +Debugging a static component is straightforward. Debugging a generated interface is detective work. + +The tooling helps, but the conceptual overhead is real. + +### Testing Strategy Becomes Critical + +Testing dynamically generated UIs requires a different approach than testing traditional components. You can't just write unit tests for a component and call it done—the component itself might be simple, but the schema that drives it can vary infinitely. + +Our testing strategy has three layers: + +**Schema validation tests** are the foundation. Every schema gets comprehensive validation tests that verify the structure itself is correct. These tests catch issues like missing required fields, invalid enum values, or type mismatches. We treat schemas as first-class code artifacts with their own test suites. + +**Contract tests** verify the relationship between schemas and components. Given a valid schema, does the component render without errors? We maintain a library of example schemas—edge cases, common patterns, minimal valid schemas—and run them through the rendering pipeline. This catches breaking changes when either schemas or components evolve. + +**Integration tests** validate the full generation pipeline. We mock the AI responses with known schemas, then verify the entire flow: context analysis → schema generation → validation → rendering. This ensures the retry logic works, fallbacks activate correctly, and error boundaries catch failures gracefully. + +We rely heavily on snapshot testing for the rendered output. When a schema changes, snapshot tests highlight exactly what UI changes resulted. This gives us confidence that schema evolution doesn't break existing interfaces unexpectedly. + +The biggest shift is acceptance that you can't test every possible variation. Instead, you test the boundaries: minimum valid schemas, maximum complexity schemas, common patterns, and known edge cases. Type safety through Zod catches most issues at compile time, and runtime validation catches the rest before users see them. + +### Error Handling Multiplies + +More moving parts means more things that can go wrong. + +AI generation can fail. Network timeout, rate limit, malformed response. Validation can fail. AI generated something that doesn't match the schema. Retry loop can exhaust. After three attempts, still no valid schema. Rendering can fail. Unknown schema type, missing required data, component error. + +You need graceful fallbacks for every failure mode. + +Default schemas that are safe to show when generation fails. Error boundaries in the frontend that catch rendering failures and show something useful. Monitoring and alerting for validation failures so you know when your prompts or schemas need adjustment. User-friendly error states—"We're having trouble generating recommendations right now, here's what we suggest generally..." + +Every dynamic system trades simplicity for resilience engineering. + +### Performance Considerations + +AI generation adds latency. LLM API calls can take seconds. You can't generate fresh schemas on every page load. + +You need strategies: pre-generation for common scenarios, caching generated schemas by context hash, background generation with optimistic UI, hybrid approaches where you show static defaults while dynamic enhancement loads, edge computing to move generation closer to users. + +But you're adding complexity to maintain responsiveness. The simple "render component with props" is now "check cache, maybe generate, validate, render, handle failure cases." + +The performance budget gets spent differently. + +### Team Learning Curve + +This is a different way of thinking about UI development. + +Your team needs to understand schema design—what makes a good schema, how to evolve them, how to version them. They need to understand runtime validation and how to write schemas that AI can reliably generate. They need to understand slot-based architecture and mapping layers. They need to understand the trade-offs between flexibility and predictability. + +Not every team wants this complexity. For simple, predictable applications, it's overkill. The traditional component-per-feature approach works fine when features are truly unique and don't follow patterns. + +You're choosing a different set of problems. More upfront design work on schemas, less repetitive component building. More system-level thinking, less feature-level implementation. + +It's not objectively better. It's different. And it requires buy-in. + +## When Does This Make Sense? + +So when should you actually consider this approach? + +**Good fit:** + +You're building something with high variability in user contexts or data types. The same underlying features need to look different for different users, different roles, different situations. + +You have frequent new requirements that follow similar patterns. Not "build a completely new feature," but "this feature needs to work slightly differently for this new context." + +Your domain is one where AI can make intelligent contextual decisions. There are patterns to learn, data to analyze, reasonable inferences to make. + +Your team is comfortable with schema-driven development. Or willing to learn. And has the capacity to manage the additional architectural complexity. + +Your users benefit meaningfully from personalized, adaptive experiences. The variability actually matters to them; it's not just engineer preference. + +**Bad fit:** + +You have pixel-perfect design requirements. Brand campaigns, marketing sites, anything where the exact visual presentation is non-negotiable. Schema-driven generation gives you flexibility, not precision. + +Your feature set is predictable and stable. If you're building ten truly unique features with no pattern between them, there's no schema to extract. Just build the ten features. + +You have a small team without capacity for schema management. The overhead might outweigh the benefits. + +You're working on performance-critical real-time interactions. The latency of generation and validation might not be acceptable. + +You have regulatory requirements for fixed UI flows. Sometimes the law requires specific workflows in specific orders. Dynamic generation adds compliance risk. + +**The litmus test:** If you find yourself building slight variations of the same component over and over—same structure, different data, different actions, different styling—you're a candidate. You have patterns worth extracting into schemas. + +If every feature is truly unique, truly bespoke, you're probably not. + +## Components Learning to Think + +Component libraries aren't dead. They're evolving. + +We're moving from tools we manually compose to systems that compose themselves based on context. + +What we built at Bambee is proof that this works in production. Dynamic wizards that adapt to what information is missing. Adaptive cards that surface different insights for different users. Contextual visualizations that show what matters most. All using our component library—Nuxt UI—just arranged by the system instead of by developers. + +But it's early. The schemas are still simple. The generation logic is straightforward. The mapping layers are manageable. + +What comes next is more interesting. + +More sophisticated schemas that can express complex layouts, responsive variations, accessibility requirements, animation preferences. Cross-domain schema standards that let us share UI patterns across completely different applications. Entire pages assembled from intent rather than files. Vector-powered asset selection that makes every interface feel custom. Progressive complexity that adapts to user capability in real-time. Mass personalization without the maintenance burden of variants. + +The vision: developers define schemas and provide component libraries. AI generates optimal UI for each user's context. Users see interfaces that feel custom-built for them. No custom building required. + +This isn't replacing the craft of UI development. It's augmenting it. Moving us from pixel-pushing to pattern-defining. From building variations to building systems that generate variations. From asking "how do I build this interface?" to asking "how do I describe the space of possible interfaces?" + +The future of UI development might not be about building interfaces. It might be about building the systems that build interfaces. + +Component libraries aren't dying. They're learning to think. \ No newline at end of file diff --git a/.firecrawl/byteiota-vite8.md b/.firecrawl/byteiota-vite8.md new file mode 100644 index 0000000..6c7db9b --- /dev/null +++ b/.firecrawl/byteiota-vite8.md @@ -0,0 +1,244 @@ +Share + +- [Share on Facebook](https://www.facebook.com/sharer.php?u=https%3A%2F%2Fbyteiota.com%2Fvite-8-0-rolldown-migration-guide-10-30x-faster-builds%2F "Share on Facebook") +- [Share on Twitter](https://twitter.com/share?url=https%3A%2F%2Fbyteiota.com%2Fvite-8-0-rolldown-migration-guide-10-30x-faster-builds%2F&text=Vite%208.0%20Rolldown%20Migration%20Guide:%2010-30x%20Faster%20Builds "Share on Twitter") + +- [Share on Linkedin](https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fbyteiota.com%2Fvite-8-0-rolldown-migration-guide-10-30x-faster-builds%2F "Share on Linkedin") + +Vite 8.0 stable dropped on March 12, 2026, replacing its dual-bundler architecture with [Rolldown](https://rolldown.rs/), a single Rust-based bundler delivering 10-30x faster builds. Linear saw production builds shrink from 46 seconds to 6 seconds—an 87% reduction. With Vite downloaded 65 million times weekly, this upgrade affects millions of developers. Here’s your migration guide. + +## Vite 8 Unifies Build Pipeline with Rolldown + +Vite 8 consolidates two bundlers into one. Previously, Vite used esbuild for fast development and Rollup for optimized production builds. This dual-bundler approach worked but created potential inconsistencies between dev and prod environments. Rolldown eliminates that split. + +Rolldown is a Rust-based bundler with full Rollup API compatibility. It matches esbuild’s development speed while delivering 10-30x faster production builds than Rollup. In official benchmarks testing 19,000 modules, Rolldown completed in 1.61 seconds versus Rollup’s 40.10 seconds—a 25x improvement. + +The architectural unification simplifies configuration. Developers no longer juggle separate esbuild and rollupOptions settings. One bundler, one config, consistent behavior across environments. + +## 10-30x Faster Builds in Real-World Projects + +Performance gains are substantial. [Linear reduced production build times by 87%](https://vite.dev/blog/announcing-vite8), dropping from 46 seconds to 6 seconds. Beehiiv’s large codebase saw 64% improvement. Mercedes-Benz.io cut build times by 38%. + +However, performance gains scale with project size. Small projects under 100 modules see 2-5x improvements. Mid-sized projects between 100-500 modules hit 5-10x. Large projects with 500+ modules achieve the advertised 10-30x gains. + +One developer testing a single-page app watched builds shrink from 3.8 seconds to 0.8 seconds—a clean 5x improvement. For large enterprise apps, these savings multiply across hundreds of daily builds, cutting hours from CI/CD pipelines. + +## How to Migrate to Vite 8 + +Migration is straightforward for most projects. Update Vite to 8.0, test locally, deploy if no issues arise. A compatibility layer auto-converts esbuild and rollupOptions configurations to Rolldown equivalents. + +Basic migration: + +```bash +# Update to Vite 8.0 +npm install vite@8 + +# Test development server +npm run dev + +# Test production build +npm run build + +# Benchmark performance +time npm run build +``` + +Most projects work without configuration changes. The compatibility layer handles the transition automatically. If you encounter issues, [Vite provides a gradual migration path](https://main.vite.dev/guide/migration): test with rolldown-vite on Vite 7 first, then upgrade to Vite 8 stable. This two-step approach isolates Rolldown-specific problems from general upgrade issues. + +## Should You Upgrade? Decision Framework + +Upgrade priority depends on project size and build frequency. Large codebases with 500+ modules benefit most—10-30x gains justify immediate migration. Teams running multiple builds daily see compounding time savings. If CI/CD pipelines take 40 seconds per build, Rolldown cuts that to 2 seconds, saving 38 seconds × 100 builds = 63 minutes daily. + +Mid-sized projects between 100-500 modules should upgrade within the month. You’ll see 5-10x improvements—noticeable but not game-changing. Standard release cycles (daily or weekly deploys) make this a medium priority. + +Small projects under 100 modules see 2-5x gains. Still worthwhile, but less impactful. If you’re risk-averse or running mission-critical production apps, waiting 1-2 months for community feedback is reasonable. Let others find the edge cases first. + +Skip immediate upgrade if you rely on obscure Rollup plugins that may lack Rolldown compatibility. Check the Vite plugin registry first. Also skip if you’re on Vite 6 or older—address that gap before jumping to Vite 8. + +## Troubleshooting Common Migration Issues + +Three issues account for most migration headaches: CommonJS interop changes, manualChunks deprecation, and esbuild transform failures. + +CommonJS imports may break due to Rolldown’s stricter module handling. If runtime errors appear when importing CJS modules, add `legacy.inconsistentCjsInterop: true` to your config temporarily. Long-term, migrate to ESM or fix module resolution. This isn’t Vite’s fault—it’s exposing existing module system inconsistencies. + +The `manualChunks` config no longer works. Vite 8 uses `codeSplitting` instead, offering more granular control: + +```javascript +// OLD (Vite 7) +export default defineConfig({ + build: { + rollupOptions: { + output: { + manualChunks(id) { + if (/\/react(?:-dom)?/.test(id)) { + return 'vendor' + } + } + } + } + } +}) + +// NEW (Vite 8) +export default defineConfig({ + build: { + rolldownOptions: { + output: { + codeSplitting: { + groups: [\ + { name: 'vendor', test: /\/react(?:-dom)?/ }\ + ] + } + } + } + } +}) +``` + +Plugins using `transformWithEsbuild` will fail because esbuild is no longer bundled. Migrate to `transformWithOxc` or install esbuild manually as a peer dependency. The @vitejs/plugin-react v6 already made this switch, using Oxc for React Refresh transforms and eliminating the Babel dependency entirely. + +## What’s Next for Vite + +Vite’s roadmap includes Full Bundle Mode (experimental), which serves bundled files in development for 3× faster dev server startup, 40% faster full reloads, and 10× fewer network requests. This addresses one of Vite’s last pain points—hundreds of separate module requests in large apps. + +[VoidZero](https://voidzero.dev/posts/announcing-rolldown-rc), the team behind Vite and Rolldown, is building a unified Rust toolchain for JavaScript development. Rolldown handles bundling, Oxc powers compilation and minification, and more tools are coming. The trend is clear: Rust-based tooling is replacing JavaScript-based build tools across the ecosystem, from swc to turbopack to Biome. + +## Key Takeaways + +- Vite 8.0 stable replaces esbuild + Rollup with a single Rust bundler (Rolldown), delivering 10-30x faster production builds while maintaining plugin compatibility +- Large projects (500+ modules) see the biggest gains (10-30x), mid-sized projects hit 5-10x, small projects get 2-5x—performance scales with codebase size +- Most projects migrate without config changes thanks to auto-conversion, but CommonJS interop and manualChunks require manual fixes +- Upgrade now if you’re on Vite 7 with large codebases or frequent builds—the stable release is production-ready and compatibility is high +- Future Vite improvements (Full Bundle Mode, Rust toolchain expansion) show continued innovation, making this a safe long-term bet + +Vite 8 isn’t just faster—it’s simpler. One bundler, one config, one mental model. The dual-bundler era is over. + +### Share + +![ByteBot]() + +[ByteBot](https://byteiota.com/author/bytebot/ "Posts by ByteBot") + +I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information. + +[Previous](https://byteiota.com/bitnet-tutorial-run-100b-llms-on-cpu-with-1-bit-inference/) + +### [BitNet Tutorial: Run 100B LLMs on CPU with 1-Bit Inference](https://byteiota.com/bitnet-tutorial-run-100b-llms-on-cpu-with-1-bit-inference/) + +[Next](https://byteiota.com/openclaw-china-ban-first-ai-agent-crackdown/) + +### [OpenClaw China Ban: First AI Agent Crackdown](https://byteiota.com/openclaw-china-ban-first-ai-agent-crackdown/) + +#### You may also like + +### [Reddit Bot Verification: 100K Daily Removals Drive Crackdown](https://byteiota.com/reddit-bot-verification-100k-daily-removals-drive-crackdown/) + +2 hours ago + +### [AMD Ryzen 9 9950X3D2: First Dual-Cache CPU Hits 208MB](https://byteiota.com/amd-ryzen-9-9950x3d2-first-dual-cache-cpu-hits-208mb/) + +4 hours ago + +[![]()](https://byteiota.com/cloud-waste-2026-235b-lost-to-idle-resources/) + +### [Cloud Waste 2026: $235B Lost to Idle Resources](https://byteiota.com/cloud-waste-2026-235b-lost-to-idle-resources/) + +5 hours ago + +[![]()](https://byteiota.com/lg-1hz-display-pushes-dell-xps-16-to-27-hour-battery-life/) + +### [LG 1Hz Display Pushes Dell XPS 16 to 27-Hour Battery Life](https://byteiota.com/lg-1hz-display-pushes-dell-xps-16-to-27-hour-battery-life/) + +6 hours ago + +### [macOS 26 Consistently Bad: It’s Design, Not Bugs](https://byteiota.com/macos-26-consistently-bad-its-design-not-bugs/) + +8 hours ago + +[![]()](https://byteiota.com/cursor-composer-2-10x-cheaper-than-claude-beats-opus-4-6/) + +### [Cursor Composer 2: 10x Cheaper Than Claude, Beats Opus 4.6](https://byteiota.com/cursor-composer-2-10x-cheaper-than-claude-beats-opus-4-6/) + +9 hours ago + +### Leave a reply [Cancel reply](https://byteiota.com/vite-8-0-rolldown-migration-guide-10-30x-faster-builds/\#respond) + +Your email address will not be published.Required fields are marked \* + +Comment + +Name \* + +Email \* + +Website + +Save my name, email, and website in this browser for the next time I comment. + +Δ + +#### More in: [JavaScript](https://byteiota.com/programming/javascript/) + +[![Featured image for HTMX Tutorial 2026: Replace React with 14KB HTML]()](https://byteiota.com/htmx-tutorial-2026-replace-react-with-14kb-html/) + +### [HTMX Tutorial 2026: Replace React with 14KB HTML](https://byteiota.com/htmx-tutorial-2026-replace-react-with-14kb-html/) + +5 days ago + +[![Featured image for TanStack Start: Type-Safe React Framework for 2026]()](https://byteiota.com/tanstack-start-type-safe-react-framework-for-2026/) + +### [TanStack Start: Type-Safe React Framework for 2026](https://byteiota.com/tanstack-start-type-safe-react-framework-for-2026/) + +5 days ago + +[![Featured image for Hono Framework: Build Edge APIs on Cloudflare Workers]()](https://byteiota.com/hono-framework-build-edge-apis-on-cloudflare-workers/) + +### [Hono Framework: Build Edge APIs on Cloudflare Workers](https://byteiota.com/hono-framework-build-edge-apis-on-cloudflare-workers/) + +6 days ago + +[![Featured image for React Server Components: The Practical Guide for 2026]()](https://byteiota.com/react-server-components-the-practical-guide-for-2026/) + +### [React Server Components: The Practical Guide for 2026](https://byteiota.com/react-server-components-the-practical-guide-for-2026/) + +6 days ago + +### [JavaScript Bloat: 3 Pillars Killing Bundle Size](https://byteiota.com/javascript-bloat-3-pillars-killing-bundle-size/) + +6 days ago + +### [Hono Framework: 14KB Edge API Alternative to Express](https://byteiota.com/hono-framework-14kb-edge-api-alternative-to-express/) + +March 13, 2026 + +Next Article: + +March 13, 2026 + +min read + +-21 % + +## [![logo]()](https://byteiota.com/) + +[✕Close](https://byteiota.com/vite-8-0-rolldown-migration-guide-10-30x-faster-builds/#atbs-ceris-offcanvas-primary) + +## [![logo]()](https://byteiota.com/) + +[✕](https://byteiota.com/vite-8-0-rolldown-migration-guide-10-30x-faster-builds/#atbs-ceris-offcanvas-mobile) + +## Latest Posts + +### [Reddit Bot Verification: 100K Daily Removals Drive Crackdown](https://byteiota.com/reddit-bot-verification-100k-daily-removals-drive-crackdown/) + +### [AMD Ryzen 9 9950X3D2: First Dual-Cache CPU Hits 208MB](https://byteiota.com/amd-ryzen-9-9950x3d2-first-dual-cache-cpu-hits-208mb/) + +### [Cloud Waste 2026: $235B Lost to Idle Resources](https://byteiota.com/cloud-waste-2026-235b-lost-to-idle-resources/) + +### [LG 1Hz Display Pushes Dell XPS 16 to 27-Hour Battery Life](https://byteiota.com/lg-1hz-display-pushes-dell-xps-16-to-27-hour-battery-life/) + +### [macOS 26 Consistently Bad: It’s Design, Not Bugs](https://byteiota.com/macos-26-consistently-bad-its-design-not-bugs/) + +[![feedmatters.com](https://byteiota.com/wp-content/uploads/2026/02/promote-feedmatters-edited.png)](https://feedmatters.com/?utm_source=byteiota&utm_medium=banner&utm_campaign=popup) + +× \ No newline at end of file diff --git a/.firecrawl/corvu-dynamic.md b/.firecrawl/corvu-dynamic.md new file mode 100644 index 0000000..ecca94b --- /dev/null +++ b/.firecrawl/corvu-dynamic.md @@ -0,0 +1,112 @@ +# Dynamic Components + +All primitive components that render a DOM element are dynamic, which means that you can modify the element or the component they should render as. + +## Native elements [Section titled Native elements](https://corvu.dev/docs/dynamic-components/\#native-elements) + +In most cases, you shouldn’t need to change the DOM element that the primitive component renders. corvu has sensible defaults for all components. But there are cases where it makes sense to change them. An example would be the `Tooltip` trigger which renders as a `button` element. You may want to render a tooltip on a link (`a` tag) instead. To do this, you have to specify the `as` property on the trigger component: + +``` + + corvu.dev + +``` + +## Solid components [Section titled Solid components](https://corvu.dev/docs/dynamic-components/\#solid-components) + +A much more common use case is to render a primitive component as a custom Solid component. This is useful to apply default styling or to add additional functionality. + +For example, you might have your own, custom-styled button component and want to use it as a trigger for a dialog: + +``` +import { + ComponentProps, + splitProps, +} from 'solid-js' +import Dialog from '@corvu/dialog' + +const CustomButton = ( + props: ComponentProps<'button'> & { variant: 'fill' | 'outline' }, +) => { + const [local, rest] = splitProps(props, ['variant']) + // Apply your custom styling here + return + + Label + + + ) +} +``` + +Every primitive provides different properties in its context, have a look at the API section of each primitive to see what’s available. + +## Children callbacks [Section titled Children callbacks](https://corvu.dev/docs/state/\#children-callbacks) + +The `Root` component of every primitive (and in a few cases others) also accepts a function as its children. By doing this, we can pass the internal state to the children for you to access. An example of this looks like this: + +``` +import Dialog from '@corvu/dialog' +import { createSignal } from 'solid-js' + +const MyDialog = () => { + return ( + + {(props) => ( + <> +

The dialog is {props.open() ? 'open' : 'closed'}

+ + Open Dialog + + Label + + + )} +
+ ) +} +``` + +Note that the props passed from the `Root` component include reactive getters. Make sure to access them in a reactive scope, like you would in any other Solid component. + +## Keyed context [Section titled Keyed context](https://corvu.dev/docs/state/\#keyed-context) + +There may be situations where you want to use nested instances of the same primitive. For example, multiple dialogs with multiple trigger buttons that are hard to separate in the template. For this case, corvu allows you to pass a `contextId` to every primitive component to tell which context to use. + +Here’s how two nested dialogs would look like: + +``` + + + + Open Dialog 1 + Open Dialog 2 + + + Dialog 1 + + + + Dialog 2 + + + + +``` + +When using keyed contexts, you can pass the same key to the `useContext()` function to access the context of the respective primitive. + +``` +const { open, setOpen } = Dialog.useContext('dialog-1') +``` + +Developed and designed by [Jasmin](https://github.com/GiyoMoon/) \ No newline at end of file diff --git a/.firecrawl/corvu-styling.md b/.firecrawl/corvu-styling.md new file mode 100644 index 0000000..9c07567 --- /dev/null +++ b/.firecrawl/corvu-styling.md @@ -0,0 +1,91 @@ +# Styling + +corvu leaves the styling up to you. You can use Tailwind CSS, any CSS-in-JS library or just plain old CSS to style primitives. + +## Data attributes [Section titled Data attributes](https://corvu.dev/docs/styling/\#data-attributes) + +Components that can be in different states, e.g. `open` or `closed` for a dialog, provide data attributes to style them accordingly. + +Here is an example of how to style a dialog based on its open state: + +``` +.dialog_content[data-open] { + /* styles to apply when open */ +} +.dialog_content[data-closed] { + /* styles to apply when closed */ +} +``` + +Don’t forget to add the `dialog_content` class to your Dialog content component: + +``` +... +``` + +Additionally, every corvu component has a data attribute for you to use. A dialog content element would render like this: + +``` +
...
+``` + +You can use it to style all components of the same kind at once: + +``` +[data-corvu-dialog-content] { + /* styles to apply to the dialog content */ +} +[data-corvu-dialog-content][data-open] { + /* styles to apply when open */ +} +[data-corvu-dialog-content][data-closed] { + /* styles to apply when closed */ +} +``` + +Additionally, corvu provides plugins for these CSS frameworks: + +- [Tailwind CSS plugin](https://corvu.dev/docs/installation/#tailwind-css-plugin) +- [UnoCSS preset](https://corvu.dev/docs/installation/#unocss-preset) + +They make it easy to style components based on their current state using modifiers. + +**Available modifiers** + +- `corvu-open` -\> `&[data-open]` +- `corvu-closed` -\> `&[data-closed]` +- `corvu-expanded` -\> `&[data-expanded]` +- `corvu-collapsed` -\> `&[data-collapsed]` +- `corvu-transitioning` -\> `&[data-transitioning]` +- `corvu-opening` -\> `&[data-opening]` +- `corvu-closing` -\> `&[data-closing]` +- `corvu-snapping` -\> `&[data-snapping]` +- `corvu-resizing` -\> `&[data-resizing]` +- `corvu-disabled` -\> `&[data-disabled]` +- `corvu-active` -\> `&[data-active]` +- `corvu-dragging` -\> `&[data-dragging]` +- `corvu-selected` -\> `&[data-selected]` +- `corvu-today` -\> `&[data-today]` +- `corvu-range-start` -\> `&[data-range-start]` +- `corvu-range-end` -\> `&[data-range-end]` +- `corvu-in-range` -\> `&[data-in-range]` +- `corvu-side-top` -\> `&[data-side='top']` +- `corvu-side-right` -\> `&[data-side='right']` +- `corvu-side-bottom` -\> `&[data-side='bottom']` +- `corvu-side-left` -\> `&[data-side='left']` + +These two CSS framework use similar syntax. You can style components like this: + +``` + + ... + +``` + +## Animation [Section titled Animation](https://corvu.dev/docs/styling/\#animation) + +corvu has built-in support for CSS animations and waits for any pending animation to finish before removing an element from the DOM. This means you can use CSS animations to animate the appearance and disappearance of primitives. Every unmountable component also provides a `forceMount` property which forces it to stay mounted in the DOM even when it is not visible. This is useful when using third-party animation libraries. + +Developed and designed by [Jasmin](https://github.com/GiyoMoon/) \ No newline at end of file diff --git a/.firecrawl/csszone-anchor.md b/.firecrawl/csszone-anchor.md new file mode 100644 index 0000000..d4d3bd6 --- /dev/null +++ b/.firecrawl/csszone-anchor.md @@ -0,0 +1,79 @@ +# CSS Anchor Positioning 2026: Practical Guide for Tooltips, Menus, and Smart Overlays + +Positioning contextual UI has always been painful. Dropdowns, tooltips, and popovers often require JavaScript calculations, viewport checks, and custom collision logic. CSS Anchor Positioning moves a large part of this work into native styling. + +## Why It Matters + +- dropdown clipped on small screens +- tooltip detached from trigger on scroll +- menu appears off-screen in RTL or localized UI + +## Core Mental Model + +Define an anchor element +Attach floating UI to that anchor +Let CSS handle alignment behavior + +## Real Use Cases + +- action menu in data tables +- profile dropdown in sticky headers +- inline help tooltip in forms +- contextual edit controls in CMS + +## UX Rules + +- keep overlays close to trigger +- preserve keyboard focus flow +- add deterministic closing behavior +- avoid huge animation while repositioning + +## Rollout Strategy + +tooltip +dropdown +popover with richer content + +## Conclusion + +Anchor positioning is a structural improvement that reduces UI fragility in real products. + +## Related posts + +Continue reading on nearby topics. + +[Latest CSS Gradient Features and Trends for 2026Latest CSS gradient features for 2026: new color combinations, mesh techniques, animated transitions, and practical production patterns.](https://css-zone.com/blog/css-gradient-trends-2026) [Core Web Vitals 2026: CSS Playbook for Faster LCP, Better INP, and Stable CLSA practical Core Web Vitals 2026 guide focused on CSS architecture, rendering strategy, font loading, and layout stability for real products.](https://css-zone.com/blog/core-web-vitals-2026-css-playbook) [CSS Best Practices for Real Projects: A Practical Playbook from CSS-Zone.comA practical CSS guide for production teams: architecture, naming, tokens, responsive strategy, performance, and accessibility. Includes many copy-paste-ready examples and workflows used on CSS-Zone.com.](https://css-zone.com/blog/css-best-practices-real-projects-css-zone) [Modern CSS Features You Should Use in 2026Explore the latest CSS features that are changing web development: container queries, :has() selector, cascade layers, and more cutting-edge techniques.](https://css-zone.com/blog/modern-css-features-2026) + +## Comments + +0 + +Sign in to leave a comment. + +Sign in + +No comments yet. Be the first. + +Cookies + +## We use cookies to keep things smooth + +They help keep you signed in, remember your preferences, and measure what features land. You control what happens next. + +[Privacy Policy](https://css-zone.com/privacy-policy) [Cookie rules](https://css-zone.com/cookie-policy) + +Not nowAccept cookies + +Contact + +CSS file\_type\_scss + +reCAPTCHA + +Recaptcha requires verification. + +[Privacy](https://www.google.com/intl/en/policies/privacy/) \- [Terms](https://www.google.com/intl/en/policies/terms/) + +protected by **reCAPTCHA** + +[Privacy](https://www.google.com/intl/en/policies/privacy/) \- [Terms](https://www.google.com/intl/en/policies/terms/) \ No newline at end of file diff --git a/.firecrawl/devproportal-radix-react-aria.md b/.firecrawl/devproportal-radix-react-aria.md new file mode 100644 index 0000000..1ca3e65 --- /dev/null +++ b/.firecrawl/devproportal-radix-react-aria.md @@ -0,0 +1,382 @@ +[↓\\ +Skip to main content](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/#main-content) + +[DevPro Portal](https://devproportal.com/) + +Table of Contents + + +Table of Contents + + +It’s 2026. If you are still wrestling with `!important` overrides in Material UI or trying to hack the internal DOM structure of a Bootstrap component just to match a Figma design, you’re doing it the hard way. + +The React ecosystem has matured. We’ve moved past the era of “All-in-One” component kits that dictate your styling. We are firmly in the era of **Headless UI**. + +As senior developers and architects, our goal isn’t just to put pixels on the screen; it’s to ship accessible, robust, and performant interfaces that scale. We want full control over the CSS (likely via Tailwind or CSS-in-JS) without reinventing the complex logic required for keyboard navigation, focus management, and screen reader support. + +This article dives deep into the two heavyweights of the headless world: **Radix UI** and **React Aria**. We’ll compare them, build real components, and discuss the architectural implications of choosing one over the other. + +## The Headless Architecture [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#the-headless-architecture) + +Before we touch the code, let’s align on the mental model. “Headless” doesn’t mean “no UI”; it means “unopinionated UI”. + +In a traditional library (like AntD or MUI), the logic and the styling are coupled. In a Headless library, the library provides the **behavior** and **state**, while you provide the **rendering** and **styling**. + +Here is how the data flow looks in a modern Headless setup: + +Your Code + +Headless Layer (Radix/Aria) + +User Interaction + +State Management + +WAI-ARIA Roles + +Focus Trap / Loops + +Keyboard/Mouse Events + +Tailwind / CSS Modules + +JSX Structure + +Framer Motion + +Rendered DOM + +### Why does this matter? [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#why-does-this-matter) + +1. **Accessibility (a11y) is hard:** Implementing a fully accessible Dropdown Menu takes weeks of testing across VoiceOver, NVDA, and JAWS. Headless libraries give you this for free. +2. **Design Freedom:** You own the `className`. +3. **Bundle Size:** You only import the logic you need. + +* * * + +## Prerequisites and Environment [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#prerequisites-and-environment) + +To follow along, ensure you have a modern React environment set up. We are assuming a 2026 standard stack: + +- **Node.js:** v20+ (LTS) +- **React:** v19 +- **Styling:** Tailwind CSS v4 (or v3.4+) +- **Icons:** Lucide React (optional but recommended) + +### Setup [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#setup) + +We’ll create a lightweight sandbox. + +Copy + +```bash +# Create a Vite project +npm create vite@latest headless-demo -- --template react-ts + +# Enter directory +cd headless-demo + +# Install dependencies (We will use both for comparison) +npm install @radix-ui/react-popover @radix-ui/react-dialog react-aria-components class-variance-authority clsx tailwind-merge framer-motion + +# Start dev server +npm run dev +``` + +_Note: We included `class-variance-authority` (CVA) and `tailwind-merge`. These are the bread and butter for handling styles in headless components._ + +* * * + +## The Contenders: Radix vs. React Aria [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#the-contenders-radix-vs-react-aria) + +Choosing between these two is often the first architectural decision when building a Design System. + +| Feature | Radix UI | React Aria (Adobe) | +| --- | --- | --- | +| **Philosophy** | Component-first. Provides unstyled primitives (e.g., ``). | Hooks-first (historically), now offers Components. “Industrial Grade” a11y. | +| **API Surface** | simpler, cleaner JSX. Very “React-y”. | Extremely granular. Offers `useButton`, `useSelect`, etc., plus a new Component API. | +| **Styling** | Agnostic. Works perfectly with Tailwind. | Agnostic. The new `react-aria-components` has a specific `className` function for states (hover/focus). | +| **Animation** | Relies on external libs or CSS keyframes. Works great with Framer Motion. | Has built-in animation support in the new components API, but generally external. | +| **Mobile** | Good, but sometimes lacks nuanced touch interactions. | Best in class. Adobe tests on everything. Handles virtual keyboard quirks brilliantly. | +| **Bundle Size** | Modular. You install packages individually (e.g., `@radix-ui/react-tooltip`). | Modular, but the core logic is heavier due to extreme robustness. | + +* * * + +## Part 1: Building with Radix UI [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#part-1-building-with-radix-ui) + +Radix is generally the favorite for teams that want to move fast but maintain high quality. It powers the popular `shadcn/ui` collection. + +Let’s build a **Popover** component. This isn’t just a tooltip; it needs to handle focus trapping (optional), closing on outside clicks, and keyboard formatting. + +### The Implementation [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#the-implementation) + +We will use Tailwind for styling and `lucide-react` for an icon. + +Copy + +```tsx +// src/components/RadixPopover.tsx +import * as React from 'react'; +import * as Popover from '@radix-ui/react-popover'; +import { Settings2, X } from 'lucide-react'; +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +// Utility for cleaner classes +function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export default function UserSettingsPopover() { + return ( + + + + + + + +
+

Dimensions

+ +
+ + +
+ +
+ + +
+
+ + + + + + +
+
+
+ ); +} +``` + +### Analysis [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#analysis) + +1. **`asChild` Pattern:** Notice ``. This is Radix’s signature move. It merges the event handlers and ARIA attributes onto _your_ DOM node (` + + ` + overflow-auto rounded-lg drop-shadow-lg border border-slate-200 bg-white + w-[var(--trigger-width)] + ${isEntering ? 'animate-in fade-in zoom-in-95 duration-200' : ''} + ${isExiting ? 'animate-out fade-out zoom-out-95 duration-200' : ''} + `}> + + {['React', 'Vue', 'Svelte', 'Angular', 'Qwik'].map((item) => ( + ` + cursor-default select-none rounded px-2 py-1.5 text-sm outline-none + ${isFocused ? 'bg-blue-100 text-blue-900' : 'text-slate-700'} + ${isSelected ? 'font-semibold' : ''} + `} + > + {({ isSelected }) => ( +
+ {item} + {isSelected && } +
+ )} +
+ ))} +
+
+ + ); +} +``` + +### Analysis [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#analysis-1) + +1. **Render Props for Styling:** RAC uses render props heavily for styling classes (e.g., `className={({ isFocused }) => ...}`). This exposes the internal interaction state directly to Tailwind. You don’t need `data-` attributes; you have direct JS boolean access. +2. **Adaptive Behavior:** Adobe put incredible effort into mobile. On mobile devices, this component handles touch cancellation, scrolling behavior, and virtual keyboard avoidance better than almost any other library. +3. **Semantics:** The `` and `` components ensure correct ARIA roles (`role="listbox"`, `role="option"`) are applied, which are different from a standard navigation menu. + +* * * + +## Performance and Common Pitfalls [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#performance-and-common-pitfalls) + +When implementing Headless UI in a production environment, watch out for these traps. + +### 1\. The Bundle Size Myth [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#1-the-bundle-size-myth) + +You might think, “I’m importing a huge library!” Not really. Both libraries support tree-shaking. + +- **Radix:** You usually install specific packages (`@radix-ui/react-dialog`). +- **React Aria:** The monolithic package exports everything, but modern bundlers (Vite/Rollup/Webpack 5) shake out unused exports effectively. + +However, React Aria IS logically heavier because it includes code for edge cases you didn’t know existed (like specific screen reader bugs in older iOS versions). + +### 2\. Focus Management [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#2-focus-management) + +The number one bug in custom modals is **Focus Trapping**. + +- **Scenario:** User opens a modal. User hits `Tab`. Focus goes _behind_ the modal to the URL bar or the background content. +- **Solution:** Both libraries handle this, but you must ensure you don’t accidentally unmount the component before the closing animation finishes. Radix handles this with `data-state` and animations, but manual conditional rendering (`{isOpen && }`) without `AnimatePresence` (if using Framer) can break the focus return feature. + +### 3\. Z-Index Wars [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#3-z-index-wars) + +Both libraries use **Portals**. + +- **Trap:** If your global CSS sets a high z-index on a sticky header, your ported modal might end up under it if the portal container isn’t managed correctly. +- **Best Practice:** Create a dedicated stacking context or ensure your portal root (usually `body`) is handled correctly in your Tailwind config (`z-50` usually suffices for modals). + +* * * + +## Conclusion: Which one should you choose? [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#conclusion-which-one-should-you-choose) + +As an architect, your choice depends on your team’s priorities. + +**Choose Radix UI if:** + +- You want a developer experience that feels “native” to React. +- You are heavily invested in the Tailwind ecosystem (it pairs beautifully). +- You need to ship fast and “Very Good” accessibility is acceptable. +- You are building a standard B2B SaaS dashboard. + +**Choose React Aria if:** + +- **Accessibility is non-negotiable.** (e.g., Government, Healthcare, Education). +- You need robust touch/mobile interactions (drag and drop, swipes). +- You prefer render-props for styling logic over CSS selectors. +- You are building a complex design system that needs to last 5+ years. + +Both libraries represent the pinnacle of React component development in 2026. By separating behavior from design, they allow us to build UIs that are unique to our brand but universal in their usability. + +### Further Reading [\#](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/\#further-reading) + +- [WAI-ARIA Authoring Practices Guide (APG)](https://www.w3.org/WAI/ARIA/apg/) \- The bible for accessible patterns. +- [Radix UI Docs](https://www.radix-ui.com/) +- [React Aria Components Docs](https://react-spectrum.adobe.com/react-aria/components.html) + +**Stop reinventing the wheel. Import the wheel, and paint it whatever color you want.** + +## Related Articles + +- · [React Compiler: The End of Manual Memoization and the Era of Auto-Optimization](https://devproportal.com/frontend/react/react-compiler-eliminates-memoization/) +- · [Mastering Concurrent Rendering: A Deep Dive into Transitions and Deferring](https://devproportal.com/frontend/react/mastering-react-concurrent-mode-transitions-deferring/) +- · [React 19 Deep Dive: Mastering the Compiler, Actions, and Advanced Hooks](https://devproportal.com/frontend/react/react-19-deep-dive-compiler-actions-hooks/) +- · [State of React State: Redux Toolkit vs. Zustand vs. Signals](https://devproportal.com/frontend/react/react-state-management-showdown-redux-zustand-signals/) +- · [React 19 Architecture: Mastering the Server vs. Client Component Paradigm](https://devproportal.com/frontend/react/react-19-server-vs-client-components-guide/) + +## The Architect’s Pulse: Engineering Intelligence + +As a CTO with 21+ years of experience, I deconstruct the complexities of high-performance backends. Join our technical circle to receive weekly strategic drills on JVM internals, Go concurrency, and cloud-native resilience. No fluff, just pure architectural execution. + +Which technical challenge are you currently deconstructing? + +Option A: Master patterns in Go, Java, and Node.js. + +Option B: Deep-dive into Database internals and Sharding. + +Option C: Orchestrating resilience with K8s and Microservices. + +Option D: Deconstructing real-world architectural case studies. + +Join the Inner Circle + +[Built with Kit](https://kit.com/features/forms?utm_campaign=poweredby&utm_content=form&utm_medium=referral&utm_source=dynamic) + +[↑](https://devproportal.com/frontend/react/mastering-headless-ui-radix-vs-react-aria/#the-top "Scroll to top") + +* * * + +[![DevPro Portal Logo](https://devproportal.com/images/devproportal_banner_light.jpg)![DevPro Portal Logo](https://devproportal.com/images/devproportal_banner_dark.jpg)](https://devproportal.com/) + +© 2026 DevPro Portal. All rights reserved. + + +Powered by [Stonehenge EdTech](https://www.stonehengeedtech.com/). + + +[About Us](https://devproportal.com/about-us) [Terms of Service](https://devproportal.com/terms-of-service) [Privacy Policy](https://devproportal.com/privacy-policy) [Cookie Policy](https://devproportal.com/cookie-policy) [Support & Contact](https://devproportal.com/contact) \ No newline at end of file diff --git a/.firecrawl/dewidar-headless-patterns.md b/.firecrawl/dewidar-headless-patterns.md new file mode 100644 index 0000000..80863c9 --- /dev/null +++ b/.firecrawl/dewidar-headless-patterns.md @@ -0,0 +1,224 @@ +[Back to Blog](https://mohameddewidar.com/blog) + +# Headless UI Patterns in React + +Headless UI is a pattern that separates **logic** from **presentation**. The component controls state and behavior while leaving all markup and styling to the consumer. This pattern is used in modern design systems and UI libraries because it gives developers full visual freedom without sacrificing predictable logic. + +This article explains the pattern clearly, shows how to implement it in React, and provides real world examples that scale in production systems. + +## 1\. What Headless UI Means + +A headless component does not render its own layout. It exposes: + +- state +- actions +- event handlers +- accessibility helpers + +The consumer decides how the UI looks. + +Example of a headless toggle hook: + +``` +function useToggle(initial = false) { + const [on, setOn] = useState(initial); + return { + on, + toggle: () => setOn((v) => !v), + setOn + }; +} +``` + +Usage with custom UI: + +``` +const toggle = useToggle(); + + +``` + +The hook manages behavior. The UI is entirely customizable. + +## 2\. Why Headless UI Matters + +Headless components solve real problems: + +### Problem 1. Components that restrict styling + +Teams often fight against rigid components that force markup or CSS structures. + +### Problem 2. Hard to integrate with custom design systems + +Headless logic works with any HTML structure and any design system. + +### Problem 3. Difficult to reuse logic without duplicating UI + +The logic lives in one place. The UI can vary by use case. + +### Problem 4. Mixed concerns + +UI, behavior, and state are combined in one file. Headless UI splits these into clear boundaries. + +## 3\. Headless UI With Render Props + +Render props expose behavior through a function. + +``` +function Dropdown({ children }) { + const [open, setOpen] = useState(false); + + return children({ + open, + toggle: () => setOpen((v) => !v) + }); +} +``` + +Usage: + +``` + + {({ open, toggle }) => ( + <> + + {open &&
...
} + + )} +
+``` + +This gives complete visual control. + +## 4\. Headless UI With Context and Compound Components + +This pattern is cleaner for multi part components like modals, dropdowns, and accordions. + +### Step 1. Create context driven logic + +``` +const AccordionContext = createContext(null); + +function Accordion({ children }) { + const [open, setOpen] = useState(false); + + const value = { + open, + toggle: () => setOpen((v) => !v) + }; + + return ( + + {children} + + ); +} +``` + +### Step 2. Separate UI components + +``` +function AccordionTrigger({ children }) { + const { toggle } = useContext(AccordionContext); + return ; +} + +function AccordionContent({ children }) { + const { open } = useContext(AccordionContext); + return open ?
{children}
: null; +} +``` + +Usage: + +``` + + Show Details + Here are the details... + +``` + +The logic is shared. The UI remains flexible. + +## 5\. Building a Headless Select Component + +Logic: + +``` +function useSelect(items) { + const [selected, setSelected] = useState(null); + + return { + items, + selected, + select: (item) => setSelected(item) + }; +} +``` + +Usage: + +``` +const select = useSelect(["A", "B", "C"]); + +
    + {select.items.map((item) => ( +
  • select.select(item)}> + {item} {select.selected === item ? "(selected)" : ""} +
  • + ))} +
+``` + +## 6\. Accessibility in Headless UI + +A headless design system should expose accessibility helpers. + +``` +function useDialog() { + const [open, setOpen] = useState(false); + + return { + open, + triggerProps: { + "aria-haspopup": "dialog", + onClick: () => setOpen(true) + }, + dialogProps: { + role: "dialog", + "aria-modal": true + }, + close: () => setOpen(false) + }; +} +``` + +Usage: + +``` +const dialog = useDialog(); + + + +{dialog.open && ( +
+ Modal content + +
+)} +``` + +## 7\. Anti Patterns to Avoid + +- Forcing DOM structure +- Mixing styling inside the logic +- Bloated configuration props +- Hooks that try to manage both logic and rendering + +## Final Thoughts + +Headless UI patterns help teams build flexible and reusable components that scale with evolving design requirements. By separating logic from presentation, React applications become easier to maintain, more customizable, and more predictable. + +[Back to Blog](https://mohameddewidar.com/blog) \ No newline at end of file diff --git a/.firecrawl/json-render.md b/.firecrawl/json-render.md new file mode 100644 index 0000000..8485bc3 --- /dev/null +++ b/.firecrawl/json-render.md @@ -0,0 +1,597 @@ +The Generative UI Framework + +# AI → json-render → UI + +Generate dynamic, personalized UIs from prompts without sacrificing reliability. Predefined components and actions for safe, predictable output. + +Create a contact form with name, email, and message + +Create a login form with email and passwordBuild a feedback form with rating stars + +jsonnestedstreamcatalog + +``` +{"op":"add","path":"/root","value":"card"} +{"op":"add","path":"/elements/name","value":{"type":"Input","props":{"label":"Name","name":"name","statePath":"/form/name","checks":[{"type":"required","message":"Name is required"}]}}} +{"op":"add","path":"/elements/email","value":{"type":"Input","props":{"label":"Email","name":"email","type":"email","statePath":"/form/email","checks":[{"type":"required","message":"Email is required"},{"type":"email","message":"Please enter a valid email"}]}}} +``` + +``` +{ + "root": "card", + "state": { + "form": { + "name": "", + "email": "", + "message": "" + } + }, + "elements": { + "card": { + "type": "Card", + "props": { + "title": "Contact Us", + "maxWidth": "md" + }, + "children": [\ + "name",\ + "email"\ + ] + }, + "name": { + "type": "Input", + "props": { + "label": "Name", + "name": "name", + "statePath": "/form/name", + "checks": [\ + {\ + "type": "required",\ + "message": "Name is required"\ + }\ + ] + } + }, + "email": { + "type": "Input", + "props": { + "label": "Email", + "name": "email", + "type": "email", + "statePath": "/form/email", + "checks": [\ + {\ + "type": "required",\ + "message": "Email is required"\ + },\ + {\ + "type": "email",\ + "message": "Please enter a valid email"\ + }\ + ] + } + } + } +} +``` + +``` +{ + "state": { + "form": { + "name": "", + "email": "", + "message": "" + } + }, + "elements": { + "type": "Card", + "props": { + "title": "Contact Us", + "maxWidth": "md" + }, + "children": [\ + {\ + "type": "Input",\ + "props": {\ + "label": "Name",\ + "name": "name",\ + "statePath": "/form/name",\ + "checks": [\ + {\ + "type": "required",\ + "message": "Name is required"\ + }\ + ]\ + }\ + },\ + {\ + "type": "Input",\ + "props": {\ + "label": "Email",\ + "name": "email",\ + "type": "email",\ + "statePath": "/form/email",\ + "checks": [\ + {\ + "type": "required",\ + "message": "Email is required"\ + },\ + {\ + "type": "email",\ + "message": "Please enter a valid email"\ + }\ + ]\ + }\ + }\ + ] + } +} +``` + +components (39)actions (6) + +Accordion + +Collapsible sections. Items as \[{title, content}\]. Type 'single' (default) or 'multiple'. + +items: arraytype: enum? + +Alert + +Alert banner + +title: stringmessage: string?type: enum? + +Avatar + +User avatar with fallback initials + +src: string?name: stringsize: enum? + +Badge + +Status badge + +text: stringvariant: enum? + +BarGraph + +Vertical bar chart + +title: string?data: array + +Button + +Clickable button. Bind on.press for handler. + +label: stringvariant: enum?disabled: boolean? + +on.press + +ButtonGroup + +Segmented button group. Use { $bindState } on selected for selected value. + +buttons: arrayselected: string? + +on.change + +Cardslots: default + +Container card for content sections. Use for forms/content boxes, NOT for page headers. + +title: string?description: string?maxWidth: enum?centered: boolean? + +Carousel + +Horizontally scrollable carousel of cards. + +items: array + +Checkbox + +Checkbox input. Use { $bindState } on checked for binding. + +label: stringname: stringchecked: boolean? + +on.change + +Collapsibleslots: default + +Collapsible section with trigger. Children render inside. + +title: stringdefaultOpen: boolean? + +Dialogslots: default + +Modal dialog. Set openPath to a boolean state path. Use setState to toggle. + +title: stringdescription: string?openPath: string + +Drawerslots: default + +Bottom sheet drawer. Set openPath to a boolean state path. Use setState to toggle. + +title: stringdescription: string?openPath: string + +DropdownMenu + +Dropdown menu with trigger button and selectable items. + +label: stringitems: array + +on.select + +Gridslots: default + +Grid layout (1-6 columns) + +columns: number?gap: enum? + +Heading + +Heading text (h1-h4) + +text: stringlevel: enum? + +Image + +Placeholder image (displays alt text in a styled box) + +alt: stringwidth: number?height: number? + +Input + +Text input field. Use { $bindState } on value for two-way binding. Use checks for validation (e.g. required, email, minLength). + +label: stringname: stringtype: enum?placeholder: string?value: string?checks: array? + +on.submiton.focuson.blur + +LineGraph + +Line chart with points + +title: string?data: array + +Link + +Anchor link. Bind on.press for click handler. + +label: stringhref: string + +on.press + +Pagination + +Page navigation. Use { $bindState } on page for current page number. + +totalPages: numberpage: number? + +on.change + +Popover + +Popover that appears on click of trigger. + +trigger: stringcontent: string + +Progress + +Progress bar (value 0-100) + +value: numbermax: number?label: string? + +Radio + +Radio button group. Use { $bindState } on value for binding. + +label: stringname: stringoptions: arrayvalue: string? + +on.change + +Rating + +Star rating display + +value: numbermax: number?label: string? + +Select + +Dropdown select input. Use { $bindState } on value for binding. Use checks for validation. + +label: stringname: stringoptions: arrayplaceholder: string?value: string?checks: array? + +on.change + +Separator + +Visual separator line + +orientation: enum? + +Skeleton + +Loading placeholder skeleton + +width: string?height: string?rounded: boolean? + +Slider + +Range slider input. Use { $bindState } on value for binding. + +label: string?min: number?max: number?step: number?value: number? + +on.change + +Spinner + +Loading spinner indicator + +size: enum?label: string? + +Stackslots: default + +Flex container for layouts + +direction: enum?gap: enum?align: enum?justify: enum? + +Switch + +Toggle switch. Use { $bindState } on checked for binding. + +label: stringname: stringchecked: boolean? + +on.change + +Table + +Data table. columns: header labels. rows: 2D array of cell strings, e.g. \[\["Alice","admin"\],\["Bob","user"\]\]. + +columns: arrayrows: arraycaption: string? + +Tabs + +Tab navigation. Use { $bindState } on value for active tab binding. + +tabs: arraydefaultValue: string?value: string? + +on.change + +Text + +Paragraph text + +text: stringvariant: enum? + +Textarea + +Multi-line text input. Use { $bindState } on value for binding. Use checks for validation. + +label: stringname: stringplaceholder: string?rows: number?value: string?checks: array? + +Toggle + +Toggle button. Use { $bindState } on pressed for state binding. + +label: stringpressed: boolean?variant: enum? + +on.change + +ToggleGroup + +Group of toggle buttons. Type 'single' (default) or 'multiple'. Use { $bindState } on value. + +items: arraytype: enum?value: string? + +on.change + +Tooltip + +Hover tooltip. Shows content on hover over text. + +content: stringtext: string + +live renderstatic code + +export + +### Contact Us + +Name + +Email + +`npm install @json-render/core @json-render/react` + +[Get Started](https://json-render.dev/docs) [GitHub](https://github.com/vercel-labs/json-render) + +01 + +### Define Your Catalog + +Set the guardrails. Define which components, actions, and data bindings AI can use. + +02 + +### AI Generates + +Describe what you want. AI generates JSON constrained to your catalog. Every interface is unique. + +03 + +### Render Instantly + +Stream the response. Your components render progressively as JSON arrives. + +## Define your catalog + +Components, actions, and validation functions. + +``` +import { defineSchema, defineCatalog } from '@json-render/core'; +import { z } from 'zod'; + +const schema = defineSchema({ /* ... */ }); + +export const catalog = defineCatalog(schema, { + components: { + Card: { + props: z.object({ + title: z.string(), + description: z.string().nullable(), + }), + hasChildren: true, + }, + Metric: { + props: z.object({ + label: z.string(), + statePath: z.string(), + format: z.enum(['currency', 'percent']), + }), + }, + }, + actions: { + export: { params: z.object({ format: z.string() }) }, + }, +}); +``` + +Show all + +## AI generates JSON + +Constrained output that your components render natively. + +``` +{ + "root": "dashboard", + "elements": { + "dashboard": { + "type": "Card", + "props": { + "title": "Revenue Dashboard" + }, + "children": ["revenue"] + }, + "revenue": { + "type": "Metric", + "props": { + "label": "Total Revenue", + "statePath": "/metrics/revenue", + "format": "currency" + } + } + } +} +``` + +## Export as Code + +Export generated UI as standalone React components. No runtime dependencies required. + +### Generated UI Tree + +AI generates a JSON structure from the user's prompt. + +``` +{ + "root": "card", + "elements": { + "card": { + "type": "Card", + "props": { "title": "Revenue" }, + "children": ["metric", "chart"] + }, + "metric": { + "type": "Metric", + "props": { + "label": "Total Revenue", + "statePath": "analytics/revenue", + "format": "currency" + } + }, + "chart": { + "type": "Chart", + "props": { + "statePath": "analytics/salesByRegion" + } + } + } +} +``` + +Show all + +### Exported React Code + +Export as a standalone Next.js project with all components. + +``` +"use client"; + +import { Card, Metric, Chart } from "@/components/ui"; + +const data = { + analytics: { + revenue: 125000, + salesByRegion: [\ + { label: "US", value: 45000 },\ + { label: "EU", value: 35000 },\ + ], + }, +}; + +export default function Page() { + return ( + + + + + ); +} +``` + +Show all + +The export includes`package.json`, component files, styles, and everything needed to run independently. + +## Features + +### Generative UI + +Generate dynamic, personalized interfaces from prompts with AI + +### Guardrails + +AI can only use components you define in the catalog + +### Streaming + +Progressive rendering as JSON streams from the model + +### React & React Native + +Render on web and mobile from the same catalog and spec format + +### Data Binding + +Connect props to state with $state, $item, $index, and two-way binding + +### Code Export + +Export as standalone React code with no runtime dependencies + +## Get started + +`npm install @json-render/core @json-render/react` + +[Documentation](https://json-render.dev/docs) + +Ask AI `⌘I` \ No newline at end of file diff --git a/.firecrawl/kobalte-dialog.md b/.firecrawl/kobalte-dialog.md new file mode 100644 index 0000000..8c1f836 --- /dev/null +++ b/.firecrawl/kobalte-dialog.md @@ -0,0 +1,263 @@ +# Dialog + +A window overlaid on either the primary window or another dialog window. Content behind a modal dialog is inert, meaning that users cannot interact with it. + +## Import + +``` +Copyts +import { Dialog } from "@kobalte/core/dialog"; +// or +import { Root, Trigger, ... } from "@kobalte/core/dialog"; +// or (deprecated) +import { Dialog } from "@kobalte/core"; +``` + +``` +Copyts +import { Dialog } from "@kobalte/core/dialog"; +// or +import { Root, Trigger, ... } from "@kobalte/core/dialog"; +// or (deprecated) +import { Dialog } from "@kobalte/core"; +``` + +## Features + +- Follows the [WAI ARIA Dialog](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/) design pattern. +- Supports modal and non-modal modes. +- Provides screen reader announcements via rendered title and description. +- Focus is trapped and scrolling is blocked while it is open. +- Pressing `Esc` closes the dialog. +- Can be controlled or uncontrolled. + +## Anatomy + +The dialog consists of: + +- **Dialog:** Contains all the parts of a dialog. +- **Dialog.Trigger:** The button that opens the dialog. +- **Dialog.Portal:** Portals its children into the `body` when the dialog is open. +- **Dialog.Overlay:** The layer that covers the inert portion of the view when the dialog is open. +- **Dialog.Content:** Contains the content to be rendered when the dialog is open. +- **Dialog.CloseButton:** The button that closes the dialog. +- **Dialog.Title:** An accessible title to be announced when the dialog is opened. +- **Dialog.Description:** An optional accessible description to be announced when the dialog is opened. + +``` +Copytsx + + + + + + + + + + + +``` + +``` +Copytsx + + + + + + + + + + + +``` + +## Example + +Open + +index.tsxstyle.css + +``` +Copytsx +import { Dialog } from "@kobalte/core/dialog"; +import { CrossIcon } from "some-icon-library"; +import "./style.css"; + +function App() { + return ( + + Open + + +
+ +
+ About Kobalte + + + +
+ + Kobalte is a UI toolkit for building accessible web apps and design systems with + SolidJS. It provides a set of low-level UI components and primitives which can be the + foundation for your design system implementation. + +
+
+
+
+ ); +} +``` + +``` +Copytsx +import { Dialog } from "@kobalte/core/dialog"; +import { CrossIcon } from "some-icon-library"; +import "./style.css"; + +function App() { + return ( + + Open + + +
+ +
+ About Kobalte + + + +
+ + Kobalte is a UI toolkit for building accessible web apps and design systems with + SolidJS. It provides a set of low-level UI components and primitives which can be the + foundation for your design system implementation. + +
+
+
+
+ ); +} +``` + +## Usage + +### Default open + +An initial, uncontrolled open value can be provided using the `defaultOpen` prop. + +``` +Copytsx +... +``` + +``` +Copytsx +... +``` + +### Controlled open + +The `open` prop can be used to make the open state controlled. The `onOpenChange` event is fired when the user presses the trigger, close button or overlay, and receives the new value. + +``` +Copytsx +import { createSignal } from "solid-js"; + +function ControlledExample() { + const [open, setOpen] = createSignal(false); + + return ( + + ... + + ); +} +``` + +``` +Copytsx +import { createSignal } from "solid-js"; + +function ControlledExample() { + const [open, setOpen] = createSignal(false); + + return ( + + ... + + ); +} +``` + +## API Reference + +### Dialog + +`Dialog` is equivalent to the `Root` import from `@kobalte/core/dialog` (and deprecated `Dialog.Root`). + +| Prop | Description | +| --- | --- | +| open | `boolean`
The controlled open state of the dialog. | +| defaultOpen | `boolean`
The default open state when initially rendered. Useful when you do not need to control the open state. | +| onOpenChange | `(open: boolean) => void`
Event handler called when the open state of the dialog changes. | +| id | `string`
A unique identifier for the component. The id is used to generate id attributes for nested components. If no id prop is provided, a generated id will be used. | +| modal | `boolean`
Whether the dialog should be the only visible content for screen readers, when set to `true`:
\- interaction with outside elements will be disabled.
\- scroll will be locked.
\- focus will be locked inside the dialog content.
\- elements outside the dialog content will not be visible for screen readers. | +| preventScroll | `boolean`
Whether the scroll should be locked even if the dialog is not modal. | +| forceMount | `boolean`
Used to force mounting the dialog (portal, overlay and content) when more control is needed. Useful when controlling animation with SolidJS animation libraries. | +| translations | [`DialogIntlTranslations`](https://github.com/kobaltedev/kobalte/blob/main/packages/core/src/dialog/dialog.intl.ts)
Localization strings. | + +### Dialog.Trigger + +`Dialog.Trigger` consists of [Button](https://kobalte.dev/docs/core/components/button). + +| Data attribute | Description | +| --- | --- | +| data-expanded | Present when the dialog is open. | +| data-closed | Present when the dialog is close. | + +`Dialog.Content` and `Dialog.Overlay` shares the same data-attributes. + +### Dialog.Content + +| Prop | Description | +| --- | --- | +| onOpenAutoFocus | `(event: Event) => void`
Event handler called when focus moves into the component after opening. It can be prevented by calling `event.preventDefault`. | +| onCloseAutoFocus | `(event: Event) => void`
Event handler called when focus moves to the trigger after closing. It can be prevented by calling `event.preventDefault`. | +| onEscapeKeyDown | `(event: KeyboardEvent) => void`
Event handler called when the escape key is down. It can be prevented by calling `event.preventDefault`. | +| onPointerDownOutside | `(event: PointerDownOutsideEvent) => void`
Event handler called when a pointer event occurs outside the bounds of the component. It can be prevented by calling `event.preventDefault`. | +| onFocusOutside | `(event: FocusOutsideEvent) => void`
Event handler called when the focus moves outside the bounds of the component. It can be prevented by calling `event.preventDefault`. | +| onInteractOutside | `(event: InteractOutsideEvent) => void`
Event handler called when an interaction (pointer or focus event) happens outside the bounds of the component. It can be prevented by calling `event.preventDefault`. | + +## Rendered elements + +| Component | Default rendered element | +| --- | --- | +| `Dialog` | none | +| `Dialog.Trigger` | `button` | +| `Dialog.Portal` | `Portal` | +| `Dialog.Overlay` | `div` | +| `Dialog.Content` | `div` | +| `Dialog.CloseButton` | `button` | +| `Dialog.Title` | `h2` | +| `Dialog.Description` | `p` | + +## Accessibility + +### Keyboard Interactions + +| Key | Description | +| --- | --- | +| `Space` | When focus is on the trigger, opens/closes the dialog. | +| `Enter` | When focus is on the trigger, opens/closes the dialog. | +| `Tab` | Moves focus to the next focusable element. | +| `Shift` \+ `Tab` | Moves focus to the previous focusable element. | +| `Esc` | Closes the dialog and moves focus to the trigger. | + +Previous[←Context Menu](https://kobalte.dev/docs/core/components/context-menu)Next[Dropdown Menu→](https://kobalte.dev/docs/core/components/dropdown-menu) \ No newline at end of file diff --git a/.firecrawl/kobalte-intro.md b/.firecrawl/kobalte-intro.md new file mode 100644 index 0000000..8477f37 --- /dev/null +++ b/.firecrawl/kobalte-intro.md @@ -0,0 +1,29 @@ +# Introduction + +Kobalte is a UI toolkit for building accessible web apps and design systems with SolidJS. It provides a set of low-level UI components and primitives which can be the foundation for your design system implementation. + +## Key features + +### Accessible + +Components follow the [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) whenever possible. Kobalte handle accessibility implementation details like ARIA attributes, focus management, and keyboard navigation. + +### Composable + +Kobalte provides granular access to each component parts, so you can wrap them and add your own event listeners, props, etc. + +### Unstyled + +Components are shipped with zero styles, allowing you to completely customize the look and feel. Bring your preferred styling solution (vanilla CSS, Tailwind, CSS-in-JS libraries, etc...). + +## Acknowledgment + +Kobalte would not have been possible without the prior art done by other meaningful projects from the frontend community including: + +- Ariakit - [https://ariakit.org/](https://ariakit.org/) +- Radix UI - [https://www.radix-ui.com/](https://www.radix-ui.com/) +- React Aria - [https://react-spectrum.adobe.com/react-aria/](https://react-spectrum.adobe.com/react-aria/) +- Zag - [https://zagjs.com/](https://zagjs.com/) +- corvu - [https://corvu.dev/](https://corvu.dev/) + +Next[Getting started→](https://kobalte.dev/docs/core/overview/getting-started) \ No newline at end of file diff --git a/.firecrawl/kobalte-polymorphism.md b/.firecrawl/kobalte-polymorphism.md new file mode 100644 index 0000000..19f1e55 --- /dev/null +++ b/.firecrawl/kobalte-polymorphism.md @@ -0,0 +1,355 @@ +# Polymorphism + +All component parts that render a DOM element have an `as` prop. + +## The `as` prop + +For simple use cases the `as` prop can be used, either with native HTML elements or custom Solid components: + +``` +Copytsx +import { Tabs } from "@kobalte/core/tabs"; +import { MyCustomButton } from "./components"; + +function App() { + return ( + + + {/* Render an anchor tag instead of the default button */} + + A Trigger + + + {/* Render MyCustomButton instead of the default button */} + + Custom Button Trigger + + + Content one + + ); +} +``` + +``` +Copytsx +import { Tabs } from "@kobalte/core/tabs"; +import { MyCustomButton } from "./components"; + +function App() { + return ( + + + {/* Render an anchor tag instead of the default button */} + + A Trigger + + + {/* Render MyCustomButton instead of the default button */} + + Custom Button Trigger + + + Content one + + ); +} +``` + +## The `as` prop callback + +For more advanced use cases the `as` prop can accept a callback. +The main reason to use a callback over the normal `as` prop is being able to set props without interfering with Kobalte. + +When using this pattern the following rules apply to the callback: + +- You must spread the props forwarded to your callback onto your node/component. +- Custom props are passed as is from the parent. +- Kobalte options are not passed to the callback, only the resulting html attributes. +- You should set your event handlers on the parent and not inside your callback. + +``` +Copytsx +import { Tabs } from "@kobalte/core/tabs"; +import { MyCustomButton } from "./components"; + +function App() { + return ( + + + {/* The `value` prop is used by Kobalte and not passed to MyCustomButton */} + + A Trigger + + + {/* The `value` prop is used by Kobalte and not passed to MyCustomButton */} + ( + // The `value` prop is directly passed to MyCustomButton + + )} + > + Custom Button Trigger + + + Content one + + ); +} +``` + +``` +Copytsx +import { Tabs } from "@kobalte/core/tabs"; +import { MyCustomButton } from "./components"; + +function App() { + return ( + + + {/* The `value` prop is used by Kobalte and not passed to MyCustomButton */} + + A Trigger + + + {/* The `value` prop is used by Kobalte and not passed to MyCustomButton */} + ( + // The `value` prop is directly passed to MyCustomButton + + )} + > + Custom Button Trigger + + + Content one + + ); +} +``` + +You can optionally use a type helper to get the exact types passed to your callback: + +``` +Copytsx +import { Tabs, TabsTriggerOptions, TabsTriggerRenderProps } from "@kobalte/core/tabs"; +import { PolymorphicCallbackProps } from "@kobalte/core/polymorphic"; + +, + ) => ( + // The `value` prop is directly passed to MyCustomButton + + )} +> + Custom Button Trigger +; +``` + +``` +Copytsx +import { Tabs, TabsTriggerOptions, TabsTriggerRenderProps } from "@kobalte/core/tabs"; +import { PolymorphicCallbackProps } from "@kobalte/core/polymorphic"; + +, + ) => ( + // The `value` prop is directly passed to MyCustomButton + + )} +> + Custom Button Trigger +; +``` + +## Event lifecycle + +Setting custom event handlers on component will call your custom handler before Kobalte's. + +## Types + +This section is mainly for library author that want to build on top of Kobalte and expose the correct types +to your end users. + +Every component that renders an HTML element has the following types: + +- `ComponentOptions` +- `ComponentCommonProps` +- `ComponentRenderProps` +- `ComponentProps` + +For example, `Tabs.Trigger` has the types `TabsTriggerOptions`, `TabsTriggerCommonProps`, +`TabsTriggerRenderProps` and `TabsTriggerProps`. + +Components themselves accept props as `PolymorphicProps` where `T` is a generic +that extends `ValidComponent` and `ComponentProps` are the props of the Kobalte component. +This type allows components to accept Kobalte's props and all other props accepted by `T`. + +### `ComponentOptions` + +This type contains all custom props consumed by Kobalte, these props do not exist in HTML. +These are not passed to the HTML element nor to the `as` callback. + +### `ComponentCommonProps` + +This type contains HTML attributes optionally accepted by the Kobalte component and will +be forwarded to the rendered DOM node. These are managed by Kobalte but can be customized by the end +user. It includes attributes such as `id`, `ref`, event handlers, etc. The generic is used by `ref` and event handlers, +by default it is `HTMLElement`. + +### `ComponentRenderProps` + +This type extends `ComponentCommonProps` and additionally contains attributes that are passed +to the DOM node and fully managed by Kobalte. You should never assign these yourself or set them on +the Kobalte component. Modifying these props will break your component's behavior and accessibility. + +### `ComponentProps` + +This is the final type exported by components, it is equal to `ComponentOptions & Partial`. +It combines all props expected by Kobalte's component. The generic is used by the CommonProps, by default it is `HTMLElement`. + +### `PolymorphicProps` + +If you're writing a custom component and want to expose Kobalte's `as` prop to the end user +and keep proper typing, be sure to use `PolymorphicProps` for your props type. + +``` +Copytsx +import { Tabs, TabsTriggerProps } from "@kobalte/core/tabs"; +import { PolymorphicProps } from "@kobalte/core/polymorphic"; + +// Optionally extend `TabsTriggerProps` if you wish to +// expose Kobalte props to your end user. +interface CustomProps extends TabsTriggerProps { + variant: "default" | "outline"; +} + +// Your generic `T` should extend ValidComponent and have a default value of the default DOM node. +function CustomTabsTrigger( + props: PolymorphicProps>, +) { + // Typescript degrades typechecking when using generics, as long as we + // spread `others` to our element, we can effectively ignore them. + const [local, others] = splitProps(props as CustomProps, ["variant"]); + + return ( + + ); +} +``` + +``` +Copytsx +import { Tabs, TabsTriggerProps } from "@kobalte/core/tabs"; +import { PolymorphicProps } from "@kobalte/core/polymorphic"; + +// Optionally extend `TabsTriggerProps` if you wish to +// expose Kobalte props to your end user. +interface CustomProps extends TabsTriggerProps { + variant: "default" | "outline"; +} + +// Your generic `T` should extend ValidComponent and have a default value of the default DOM node. +function CustomTabsTrigger( + props: PolymorphicProps>, +) { + // Typescript degrades typechecking when using generics, as long as we + // spread `others` to our element, we can effectively ignore them. + const [local, others] = splitProps(props as CustomProps, ["variant"]); + + return ( + + ); +} +``` + +If you do not wish to allow changing the element type, you can simplify your types by making +props: `OverrideComponentProps<"button", CustomProps>`, replace `"button"` with the correct +tagname for other components, imported from `"@kobalte/utils"`. + +If you also want to export exact types, you can re-export and extends component types: + +``` +Copytsx +export interface CustomTabsTriggerOptions extends TabsTriggerOptions { + variant: "default" | "outline"; +} + +export interface CustomTabsTriggerCommonProps extends TabsTriggerCommonProps { + // If you allow users to set classes and extend them. + //class: string; +} + +export interface CustomTabsTriggerRenderProps + extends CustomTabsTriggerCommonProps, + TabsTriggerRenderProps { + // If you do not allow users to set classes and manage all of them. + class: string; +} + +export type CustomTabsTriggerProps = CustomTabsTriggerOptions & + Partial>>; + +export function CustomTabsTrigger( + props: PolymorphicProps, +) {} +``` + +``` +Copytsx +export interface CustomTabsTriggerOptions extends TabsTriggerOptions { + variant: "default" | "outline"; +} + +export interface CustomTabsTriggerCommonProps extends TabsTriggerCommonProps { + // If you allow users to set classes and extend them. + //class: string; +} + +export interface CustomTabsTriggerRenderProps + extends CustomTabsTriggerCommonProps, + TabsTriggerRenderProps { + // If you do not allow users to set classes and manage all of them. + class: string; +} + +export type CustomTabsTriggerProps = CustomTabsTriggerOptions & + Partial>>; + +export function CustomTabsTrigger( + props: PolymorphicProps, +) {} +``` + +`ElementOf` is a helper from `"@kobalte/core/polymorphic"` that converts a tag name into its element +(e.g. `ElementOf<"button"> = HTMLButtonElement`). + +Previous[←Animation](https://kobalte.dev/docs/core/overview/animation)Next[Server side rendering→](https://kobalte.dev/docs/core/overview/ssr) \ No newline at end of file diff --git a/.firecrawl/kobalte-select.md b/.firecrawl/kobalte-select.md new file mode 100644 index 0000000..8389aa4 --- /dev/null +++ b/.firecrawl/kobalte-select.md @@ -0,0 +1,1464 @@ +# Select + +Displays a list of options for the user to pick from — triggered by a button. + +## Import + +``` +Copyts +import { Select } from "@kobalte/core/select"; +// or +import { Root, Label, ... } from "@kobalte/core/select"; +// or (deprecated) +import { Select } from "@kobalte/core"; +``` + +``` +Copyts +import { Select } from "@kobalte/core/select"; +// or +import { Root, Label, ... } from "@kobalte/core/select"; +// or (deprecated) +import { Select } from "@kobalte/core"; +``` + +## Features + +- Exposed to assistive technology as a button with a listbox popup using the [WAI ARIA Listbox](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/) design pattern. +- Support for single and multiple selection. +- Support for disabled options. +- Labeling support for accessibility. +- Support for description and error message help text linked to the button via ARIA. +- Tab stop focus management. +- Keyboard support for opening the listbox using the arrow keys, including automatically focusing the first or last item accordingly. +- Typeahead to allow selecting options by typing text, even without opening the listbox. +- Browser autofill integration via a hidden native ` + + + + + + + + + + + + + + + +``` + +``` +Copytsx + +``` + +## Example + +Select a fruit…Sort + +index.tsxstyle.css + +``` +Copytsx +import { Select } from "@kobalte/core/select"; +import { CaretSortIcon, CheckIcon } from "some-icon-library"; +import "./style.css"; + +function App() { + return ( + + ); +} +``` + +``` +Copytsx +import { Select } from "@kobalte/core/select"; +import { CaretSortIcon, CheckIcon } from "some-icon-library"; +import "./style.css"; + +function App() { + return ( + + ); +} +``` + +## Usage + +### Default value + +An initial, uncontrolled value can be provided using the `defaultValue` prop, which accepts a value corresponding with the `options`. + +BlueberrySort + +``` +Copytsx + +``` + +``` +Copytsx + +``` + +### Controlled value + +The `value` prop, which accepts a value corresponding with the `options` prop, can be used to make the value controlled. The `onChange` event is fired when the user selects an option, and receives the selected option. + +BlueberrySort + +Your favorite fruit is: Blueberry. + +``` +Copytsx +import { createSignal } from "solid-js"; + +export function ControlledExample() { + const [value, setValue] = createSignal("Blueberry"); + + return ( + <> + +

Your favorite fruit is: {value()}.

+ + ); +} +``` + +``` +Copytsx +import { createSignal } from "solid-js"; + +export function ControlledExample() { + const [value, setValue] = createSignal("Blueberry"); + + return ( + <> + +

Your favorite fruit is: {value()}.

+ + ); +} +``` + +### Description + +The `Select.Description` component can be used to associate additional help text with a select. + +Select a fruit…Sort + +Choose the fruit you like the most. + +``` +Copytsx + +``` + +``` +Copytsx + +``` + +### Error message + +The `Select.ErrorMessage` component can be used to help the user fix a validation error. It should be combined with the `validationState` prop to semantically mark the select as invalid for assistive technologies. + +By default, it will render only when the `validationState` prop is set to `invalid`, use the `forceMount` prop to always render the error message (ex: for usage with animation libraries). + +GrapesSort + +Hmm, I prefer apples. + +``` +Copytsx +import { createSignal } from "solid-js"; + +export function ErrorMessageExample() { + const [value, setValue] = createSignal("Grapes"); + + return ( + + ); +} +``` + +``` +Copytsx +import { createSignal } from "solid-js"; + +export function ErrorMessageExample() { + const [value, setValue] = createSignal("Grapes"); + + return ( + + ); +} +``` + +### HTML forms + +The select `name` prop, paired with the `Select.HiddenSelect` component, can be used for integration with HTML forms. + +AppleBananaBlueberryGrapesPineapple + +Select a fruit…Sort + +ResetSubmit + +``` +Copytsx +function HTMLFormExample() { + const onSubmit = (e: SubmitEvent) => { + // handle form submission. + }; + + return ( +
+ +
+ + +
+
+ ); +} +``` + +``` +Copytsx +function HTMLFormExample() { + const onSubmit = (e: SubmitEvent) => { + // handle form submission. + }; + + return ( +
+ +
+ + +
+
+ ); +} +``` + +### Using object as options + +Objects can be used as options instead of plain strings. In this case you have to tell the select how it should work with the provided options. For this you **have to use** the following props : + +- `optionValue`: The property name to use as the value of an option (submitted with `
`). +- `optionTextValue`: The property name to use as the text value of an option for keyboard navigation. +- `optionDisabled`: The property name to use as the disabled flag of an option. + +Select a food…Sort + +``` +Copytsx +interface Fruit { + value: string; + label: string; + disabled: boolean; +} + +const options: Fruit[] = [\ + { value: "apple", label: "Apple", disabled: false },\ + { value: "banana", label: "Banana", disabled: false },\ + { value: "blueberry", label: "Blueberry", disabled: false },\ + { value: "grapes", label: "Grapes", disabled: true },\ + { value: "pineapple", label: "Pineapple", disabled: false },\ +]; + +function ObjectExample() { + return ( + + ); +} +``` + +``` +Copytsx +interface Fruit { + value: string; + label: string; + disabled: boolean; +} + +const options: Fruit[] = [\ + { value: "apple", label: "Apple", disabled: false },\ + { value: "banana", label: "Banana", disabled: false },\ + { value: "blueberry", label: "Blueberry", disabled: false },\ + { value: "grapes", label: "Grapes", disabled: true },\ + { value: "pineapple", label: "Pineapple", disabled: false },\ +]; + +function ObjectExample() { + return ( + + ); +} +``` + +### Using option groups + +When using option groups you have to tell the select how to distinguish an option from a group. For this you **have to use** the following props : + +- `optionGroupChildren`: The property name that refers to the children options of an option group. + +Additionally, the `sectionComponent` prop is used to display the option group label in the select. + +Select a food…Sort + +``` +Copytsx +interface Food { + value: string; + label: string; + disabled: boolean; +} + +interface Category { + label: string; + options: Food[]; +} + +const options: Category[] = [\ + {\ + label: "Fruits",\ + options: [\ + { value: "apple", label: "Apple", disabled: false },\ + { value: "banana", label: "Banana", disabled: false },\ + { value: "blueberry", label: "Blueberry", disabled: false },\ + { value: "grapes", label: "Grapes", disabled: true },\ + { value: "pineapple", label: "Pineapple", disabled: false },\ + ],\ + },\ + {\ + label: "Meat",\ + options: [\ + { value: "beef", label: "Beef", disabled: false },\ + { value: "chicken", label: "Chicken", disabled: false },\ + { value: "lamb", label: "Lamb", disabled: false },\ + { value: "pork", label: "Pork", disabled: false },\ + ],\ + },\ +]; + +function OptionGroupExample() { + return ( + + options={options} + optionValue="value" + optionTextValue="label" + optionDisabled="disabled" + optionGroupChildren="options" + placeholder="Select a food…" + itemComponent={props => ( + + {props.item.rawValue.label} + + + + + )} + sectionComponent={props => {props.section.rawValue.label}} + > + + >{state => state.selectedOption().label} + + + + + + + + + + + ); +} +``` + +``` +Copytsx +interface Food { + value: string; + label: string; + disabled: boolean; +} + +interface Category { + label: string; + options: Food[]; +} + +const options: Category[] = [\ + {\ + label: "Fruits",\ + options: [\ + { value: "apple", label: "Apple", disabled: false },\ + { value: "banana", label: "Banana", disabled: false },\ + { value: "blueberry", label: "Blueberry", disabled: false },\ + { value: "grapes", label: "Grapes", disabled: true },\ + { value: "pineapple", label: "Pineapple", disabled: false },\ + ],\ + },\ + {\ + label: "Meat",\ + options: [\ + { value: "beef", label: "Beef", disabled: false },\ + { value: "chicken", label: "Chicken", disabled: false },\ + { value: "lamb", label: "Lamb", disabled: false },\ + { value: "pork", label: "Pork", disabled: false },\ + ],\ + },\ +]; + +function OptionGroupExample() { + return ( + + options={options} + optionValue="value" + optionTextValue="label" + optionDisabled="disabled" + optionGroupChildren="options" + placeholder="Select a food…" + itemComponent={props => ( + + {props.item.rawValue.label} + + + + + )} + sectionComponent={props => {props.section.rawValue.label}} + > + + >{state => state.selectedOption().label} + + + + + + + + + + + ); +} +``` + +Notice the usage of generics on `Select` for proper TypeScript support. + +### Multiple selection + +The `multiple` prop can be used to create a select that allow multi-selection. In this case the value provided to `value`, `defaultValue` and `onChange` props is of type `Array`. + +The `Select.Value` children _render prop_ expose an array of selected options, and two method for removing an option from the selection and clear the selection. + +Additionally, the example below uses the `as` prop to render a `div` for the `Select.Trigger` since HTML button can't contain interactive elements according to the [W3C](https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element). + +BlueberryCrossGrapesCross +CrossSort + +Your favorite fruits are: Blueberry, Grapes. + +``` +Copytsx +import { createSignal } from "solid-js"; + +function MultipleSelectionExample() { + const [values, setValues] = createSignal(["Blueberry", "Grapes"]); + + return ( + <> + + multiple + value={values()} + onChange={setValues} + options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]} + placeholder="Select some fruits…" + itemComponent={props => ( + + {props.item.rawValue} + + + + + )} + > + + > + {state => ( + <> +
+ + {option => ( + e.stopPropagation()}> + {option} + + + )} + +
+ + + )} + + + + +
+ + + + + + +

Your favorite fruits are: {values().join(", ")}.

+ + ); +} +``` + +``` +Copytsx +import { createSignal } from "solid-js"; + +function MultipleSelectionExample() { + const [values, setValues] = createSignal(["Blueberry", "Grapes"]); + + return ( + <> + + multiple + value={values()} + onChange={setValues} + options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]} + placeholder="Select some fruits…" + itemComponent={props => ( + + {props.item.rawValue} + + + + + )} + > + + > + {state => ( + <> +
+ + {option => ( + e.stopPropagation()}> + {option} + + + )} + +
+ + + )} + + + + +
+ + + + + + +

Your favorite fruits are: {values().join(", ")}.

+ + ); +} +``` + +### Virtual scrolling + +When dealing with large collection of items, it's recommended to use a virtual scrolling solution to improve performance. + +While Kobalte doesn't provide any built-in virtual scrolling API, it can easily be integrated with a 3rd party library. The example below demonstrate how to virtualize an array of 100,000 options using the [`@tanstack/solid-virtual`](https://tanstack.com/virtual/v3) package. + +Select an item…Sort + +``` +Copytsx +import { Select } from "@kobalte/core/select"; +import { createVirtualizer } from "@tanstack/solid-virtual"; + +interface Item { + value: string; + label: string; + disabled: boolean; +} + +const options: Item[] = Array.from({ length: 100_000 }, (_, i) => ({ + value: `${i}`, + label: `Item #${i + 1}`, + disabled: false, +})); + +function SelectContent(props: { options: Item[] }) { + let listboxRef: HTMLUListElement | undefined; + + const virtualizer = createVirtualizer({ + count: props.options.length, + getScrollElement: () => listboxRef, + getItemKey: (index: number) => props.options[index].value, + estimateSize: () => 32, + enableSmoothScroll: false, + overscan: 5, + }); + + return ( + + + virtualizer.scrollToIndex(props.options.findIndex(option => option.value === key)) + } + style={{ height: "200px", width: "100%", overflow: "auto" }} + > + {items => ( +
+ + {virtualRow => { + const item = items().getItem(virtualRow.key); + + if (item) { + return ( + + {item.rawValue.label} + + + + + ); + } + }} + +
+ )} +
+
+ ); +} + +function VirtualizedExample() { + return ( + + ); +} +``` + +``` +Copytsx +import { Select } from "@kobalte/core/select"; +import { createVirtualizer } from "@tanstack/solid-virtual"; + +interface Item { + value: string; + label: string; + disabled: boolean; +} + +const options: Item[] = Array.from({ length: 100_000 }, (_, i) => ({ + value: `${i}`, + label: `Item #${i + 1}`, + disabled: false, +})); + +function SelectContent(props: { options: Item[] }) { + let listboxRef: HTMLUListElement | undefined; + + const virtualizer = createVirtualizer({ + count: props.options.length, + getScrollElement: () => listboxRef, + getItemKey: (index: number) => props.options[index].value, + estimateSize: () => 32, + enableSmoothScroll: false, + overscan: 5, + }); + + return ( + + + virtualizer.scrollToIndex(props.options.findIndex(option => option.value === key)) + } + style={{ height: "200px", width: "100%", overflow: "auto" }} + > + {items => ( +
+ + {virtualRow => { + const item = items().getItem(virtualRow.key); + + if (item) { + return ( + + {item.rawValue.label} + + + + + ); + } + }} + +
+ )} +
+
+ ); +} + +function VirtualizedExample() { + return ( + + ); +} +``` + +### Origin-aware animations + +We expose a CSS custom property `--kb-select-content-transform-origin` which can be used to animate the content from its computed origin. + +``` +Copycss +/* style.css */ +.select__content { + transform-origin: var(--kb-select-content-transform-origin); + animation: contentHide 250ms ease-in forwards; +} + +.select__content[data-expanded] { + animation: contentShow 250ms ease-out; +} + +@keyframes contentShow { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes contentHide { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-8px); + } +} +``` + +``` +Copycss +/* style.css */ +.select__content { + transform-origin: var(--kb-select-content-transform-origin); + animation: contentHide 250ms ease-in forwards; +} + +.select__content[data-expanded] { + animation: contentShow 250ms ease-out; +} + +@keyframes contentShow { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes contentHide { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-8px); + } +} +``` + +## API Reference + +### Select + +`Select` is equivalent to the `Root` import from `@kobalte/core/select` (and deprecated `Select.Root`). + +| Prop | Description | +| --- | --- | +| options | `Array`
An array of options to display as the available options. | +| optionValue | `keyof T | ((option: T) => string | number)`
Property name or getter function to use as the value of an option. This is the value that will be submitted when the select is part of a ``. | +| optionTextValue | `keyof T | ((option: T) => string)`
Property name or getter function to use as the text value of an option for typeahead purpose. | +| optionDisabled | `keyof T | ((option: T) => boolean)`
Property name or getter function to use as the disabled flag of an option. | +| optionGroupChildren | `keyof U`
Property name that refers to the children options of an option group. | +| itemComponent | `Component>`
When NOT virtualized, the component to render as an item in the `Select.Listbox`. | +| sectionComponent | `Component>`
When NOT virtualized, the component to render as a section in the `Select.Listbox`. | +| multiple | `boolean`
Whether the select allows multi-selection. | +| placeholder | `JSX.Element`
The content that will be rendered when no value or defaultValue is set. | +| value | `T | Array`
The controlled value of the select. | +| defaultValue | `T | Array`
The value of the select when initially rendered. Useful when you do not need to control the value. | +| onChange | `(value: T | Array) => void`
Event handler called when the value changes. | +| open | `boolean`
The controlled open state of the select. | +| defaultOpen | `boolean`
The default open state when initially rendered. Useful when you do not need to control the open state. | +| onOpenChange | `(open: boolean) => void`
Event handler called when the open state of the select changes. | +| allowDuplicateSelectionEvents | `boolean`
Whether `onChange` should fire even if the new value is the same as the last. | +| disallowEmptySelection | `boolean`
Whether the select allows empty selection or not. | +| closeOnSelection | `boolean`
Whether the select closes after selection. | +| selectionBehavior | `'toggle' | 'replace'`
How selection should behave in the select. | +| virtualized | `boolean`
Whether the select uses virtual scrolling. | +| modal | `boolean`
Whether the select should be the only visible content for screen readers, when set to `true`:
\- interaction with outside elements will be disabled.
\- scroll will be locked.
\- focus will be locked inside the select content.
\- elements outside the select content will not be visible for screen readers. | +| preventScroll | `boolean`
Whether the scroll should be locked even if the select is not modal. | +| forceMount | `boolean`
Used to force mounting the select (portal, positioner and content) when more control is needed. Useful when controlling animation with SolidJS animation libraries. | +| name | `string`
The name of the select. Submitted with its owning form as part of a name/value pair. | +| validationState | `'valid' | 'invalid'`
Whether the select should display its "valid" or "invalid" visual styling. | +| required | `boolean`
Whether the user must select an item before the owning form can be submitted. | +| disabled | `boolean`
Whether the select is disabled. | +| readOnly | `boolean`
Whether the select items can be selected but not changed by the user. | +| autoComplete | `string`
Describes the type of autocomplete functionality the input should provide if any. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefautocomplete) | + +`Select` also accepts the following props to customize the placement of the `Select.Content`. + +| Prop | Description | +| --- | --- | +| placement | `Placement`
The placement of the select content. | +| gutter | `number`
The distance between the select content and the trigger element. | +| shift | `number`
The skidding of the select content along the trigger element. | +| flip | `boolean | string`
Controls the behavior of the select content when it overflows the viewport:
\- If a `boolean`, specifies whether the select content should flip to the opposite side when it overflows.
\- If a `string`, indicates the preferred fallback placements when it overflows.
The placements must be spaced-delimited, e.g. "top left". | +| slide | `boolean`
Whether the select content should slide when it overflows. | +| overlap | `boolean`
Whether the select content can overlap the trigger element when it overflows. | +| sameWidth | `boolean`
Whether the select content should have the same width as the trigger element. This will be exposed to CSS as `--kb-popper-anchor-width`. | +| fitViewport | `boolean`
Whether the select content should fit the viewport. If this is set to true, the select content will have `maxWidth` and `maxHeight` set to the viewport size. This will be exposed to CSS as `--kb-popper-available-width` and `--kb-popper-available-height`. | +| hideWhenDetached | `boolean`
Whether to hide the select content when the trigger element becomes occluded. | +| detachedPadding | `number`
The minimum padding in order to consider the trigger element occluded. | +| arrowPadding | `number`
The minimum padding between the arrow and the select content corner. | +| overflowPadding | `number`
The minimum padding between the select content and the viewport edge. This will be exposed to CSS as `--kb-popper-overflow-padding`. | + +| Data attribute | Description | +| --- | --- | +| data-valid | Present when the select is valid according to the validation rules. | +| data-invalid | Present when the select is invalid according to the validation rules. | +| data-required | Present when the user must select an item before the owning form can be submitted. | +| data-disabled | Present when the select is disabled. | +| data-readonly | Present when the select is read only. | + +`Select.Label`, `Select.Trigger`, `Select.Value`, `Select.Description` and `Select.ErrorMesssage` shares the same data-attributes. + +### Select.Trigger + +`Select.Trigger` consists of [Button](https://kobalte.dev/docs/core/components/button). + +| Data attribute | Description | +| --- | --- | +| data-expanded | Present when the select is open. | +| data-closed | Present when the select is close. | + +### Select.Value + +| Render Prop | Description | +| --- | --- | +| selectedOption | `Accessor`
The first (or only, in case of single select) selected option. | +| selectedOptions | `Accessor`
An array of selected options. It will contain only one value in case of single select. | +| remove | `(option: T) => void`
A function to remove an option from the selection. | +| clear | `() => void`
A function to clear the selection. | + +| Data attribute | Description | +| --- | --- | +| data-placeholder-shown | Present when the select placeholder is visible (no value selected). | + +### Select.Icon + +| Data attribute | Description | +| --- | --- | +| data-expanded | Present when the select is open. | +| data-closed | Present when the select is close. | + +### Select.ErrorMessage + +| Prop | Description | +| --- | --- | +| forceMount | `boolean`
Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries. | + +### Select.Content + +The popper positioner will copy the same `z-index` as the `Select.Content`. + +| Data attribute | Description | +| --- | --- | +| data-expanded | Present when the select is open. | +| data-closed | Present when the select is close. | + +### Select.Arrow + +| Prop | Description | +| --- | --- | +| size | `number`
The size of the arrow. | + +### Select.Listbox + +| Prop | Description | +| --- | --- | +| scrollRef | `Accessor`
The ref attached to the scrollable element, used to provide automatic scrolling on item focus. If not provided, defaults to the listbox. | +| scrollToItem | `(key: string) => void`
When virtualized, the Virtualizer function used to scroll to the item of the given key. | +| children | `(items: Accessor>>) => JSX.Element`
When virtualized, a map function that receives an _items_ signal representing all items and sections. | + +### Select.Item + +| Prop | Description | +| --- | --- | +| item | `CollectionNode`
The collection node to render. | + +| Data attribute | Description | +| --- | --- | +| data-disabled | Present when the item is disabled. | +| data-selected | Present when the item is selected. | +| data-highlighted | Present when the item is highlighted. | + +`Select.ItemLabel`, `Select.ItemDescription` and `Select.ItemIndicator` shares the same data-attributes. + +### Select.ItemIndicator + +| Prop | Description | +| --- | --- | +| forceMount | `boolean`
Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries. | + +## Rendered elements + +| Component | Default rendered element | +| --- | --- | +| `Select` | `div` | +| `Select.Label` | `span` | +| `Select.Description` | `div` | +| `Select.ErrorMessage` | `div` | +| `Select.Trigger` | `button` | +| `Select.Value` | `span` | +| `Select.Icon` | `span` | +| `Select.Portal` | `Portal` | +| `Select.Content` | `div` | +| `Select.Arrow` | `div` | +| `Select.Listbox` | `ul` | +| `Select.Section` | `li` | +| `Select.Item` | `li` | +| `Select.ItemLabel` | `div` | +| `Select.ItemDescription` | `div` | +| `Select.ItemIndicator` | `div` | + +## Accessibility + +### Keyboard Interactions + +| Key | Description | +| --- | --- | +| `Space` | When focus is on the trigger, opens the select and focuses the first or selected item.
When focus is on an item, selects the focused item. | +| `Enter` | When focus is on the trigger, opens the select and focuses the first or selected item.
When focus is on an item, selects the focused item. | +| `ArrowDown` | When focus is on the trigger, opens the select and focuses the first or selected item.
When focus is on an item, moves focus to the next item. | +| `ArrowUp` | When focus is on the trigger, opens the select and focuses the last or selected item.
When focus is on an item, moves focus to the previous item. | +| `Home` | When focus is on an item, moves focus to first item. | +| `End` | When focus is on an item, moves focus to last item. | +| `ArrowRight` | In `Select`, when focus is on the trigger, change the selection to the next item. | +| `ArrowLeft` | In `Select`, when focus is on the trigger, change the selection to the previous item. | +| `Shift` \+ `ArrowDown` | In `Select`, moves focus to and toggles the selected state of the next item. | +| `Shift` \+ `ArrowUp` | In `Select`, moves focus to and toggles the selected state of the previous item. | +| `Shift` \+ `Space` | In `Select`, selects contiguous items from the most recently selected item to the focused item. | +| `Ctrl` \+ `Shift` \+ `Home` | In `Select`, selects the focused item and all options up to the first item. | +| `Ctrl` \+ `Shift` \+ `End` | In `Select`, selects the focused item and all options down to the last item. | +| `Ctrl` \+ `A` | In `Select`, selects all item in the list. | +| `Esc` | Closes the select and moves focus to the trigger. | + +Previous[←Segmented Control](https://kobalte.dev/docs/core/components/segmented-control)Next[Separator→](https://kobalte.dev/docs/core/components/separator) \ No newline at end of file diff --git a/.firecrawl/kobalte-ssr.md b/.firecrawl/kobalte-ssr.md new file mode 100644 index 0000000..96b4638 --- /dev/null +++ b/.firecrawl/kobalte-ssr.md @@ -0,0 +1,10 @@ +# Server side rendering + +## Usage with SolidStart + +Kobalte works out of the box with [SolidStart](https://start.solidjs.com/). + +Kobalte has been tested with `solid-js@1.8.15` and `@solidjs/start@0.6.1`, compatibility with +other versions is not guaranteed. + +Previous[←Polymorphism](https://kobalte.dev/docs/core/overview/polymorphism)Next[Accordion→](https://kobalte.dev/docs/core/components/accordion) \ No newline at end of file diff --git a/.firecrawl/kobalte-styling.md b/.firecrawl/kobalte-styling.md new file mode 100644 index 0000000..f7b8bad --- /dev/null +++ b/.firecrawl/kobalte-styling.md @@ -0,0 +1,315 @@ +# Styling + +Kobalte components are unstyled, allowing you to completely customize the look and feel. Bring your preferred styling solution (vanilla CSS, Tailwind, CSS-in-JS libraries, etc...). + +## Styling a component part + +All components and their parts accept a `class` prop. This class will be passed through to the DOM element. You can style a component part by targeting the `class` that you provide. + +index.tsxstyle.css + +``` +Copytsx +import { Popover as KPopover } from "@kobalte/core"; +import "./style.css"; + +export const Popover = () => { + return ( + + + Open + + + ... + + + ); +}; +``` + +``` +Copytsx +import { Popover as KPopover } from "@kobalte/core"; +import "./style.css"; + +export const Popover = () => { + return ( + + + Open + + + ... + + + ); +}; +``` + +## Styling a state + +When a component or its parts can have multiple states, we automatically attach `data-*` attributes that represents the specific state. For example, a popover's trigger can have: + +- `data-expanded` — When the popover is expanded. +- `data-disabled` — When the popover is disabled. + +You can style a component state by targeting the `data-*` attributes added by Kobalte. + +style.css + +``` +Copycss +.popover__trigger[data-disabled] { + /* The popover trigger style when disabled. */ +} +``` + +``` +Copycss +.popover__trigger[data-disabled] { + /* The popover trigger style when disabled. */ +} +``` + +## Using the TailwindCSS plugin + +If you are using [TailwindCSS](https://tailwindcss.com/), you can use the `@kobalte/tailwindcss` plugin to target Kobalte's `data-*` attributes with modifiers like `ui-expanded:*`. + +### Installation + +npmyarnpnpm + +``` +Copybash +npm install @kobalte/tailwindcss +``` + +``` +Copybash +npm install @kobalte/tailwindcss +``` + +### Usage + +Add the plugin to your `tailwind.config.js` : + +``` +Copyjs +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [], + theme: { + extend: {}, + }, + plugins: [\ + // default prefix is "ui"\ + require("@kobalte/tailwindcss"),\ +\ + // or with a custom prefix:\ + require("@kobalte/tailwindcss")({ prefix: "kb" }),\ + ], +}; +``` + +``` +Copyjs +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [], + theme: { + extend: {}, + }, + plugins: [\ + // default prefix is "ui"\ + require("@kobalte/tailwindcss"),\ +\ + // or with a custom prefix:\ + require("@kobalte/tailwindcss")({ prefix: "kb" }),\ + ], +}; +``` + +Style your component: + +``` +Copytsx +import { Popover as KPopover } from "@kobalte/core/popover"; + +export const Popover = () => ( + + + Open + + ... + +); +``` + +``` +Copytsx +import { Popover as KPopover } from "@kobalte/core/popover"; + +export const Popover = () => ( + + + Open + + ... + +); +``` + +You can use the following modifiers: + +| Modifier | CSS Selector | +| --- | --- | +| `ui-valid` | `&[data-valid]` | +| `ui-invalid` | `&[data-invalid]` | +| `ui-required` | `&[data-required]` | +| `ui-disabled` | `&[data-disabled]` | +| `ui-readonly` | `&[data-readonly]` | +| `ui-checked` | `&[data-checked]` | +| `ui-indeterminate` | `&[data-indeterminate]` | +| `ui-selected` | `&[data-selected]` | +| `ui-pressed` | `&[data-pressed]` | +| `ui-expanded` | `&[data-expanded]` | +| `ui-highlighted` | `&[data-highlighted]` | +| `ui-current` | `&[data-current]` | + +It's also possible to use _inverse modifiers_ in the form of `ui-not-*`, _group and peer modifiers_ in the form of `ui-group-*` and `ui-peer-*`. + +## Using the Vanilla Extract plugin + +If you are using [Vanilla Extract](https://vanilla-extract.style/), you can use the `@kobalte/vanilla-extract` plugin to target Kobalte's `data-*` attributes. + +### Installation + +npmyarnpnpm + +``` +Copybash +npm install @kobalte/vanilla-extract +``` + +``` +Copybash +npm install @kobalte/vanilla-extract +``` + +### Usage + +Use the `componentStateStyles` utility function to create vanilla-extract styles that target `data-*` attributes of Kobalte components. + +``` +Copyts +// styles.css +import { componentStateStyles } from "@kobalte/vanilla-extract"; +import { style } from "@vanilla-extract/css"; + +const button = style([\ + {\ + background: "blue",\ + padding: "2px 6px",\ + },\ + componentStateStyles({\ + disabled: {\ + opacity: 0.4,\ + },\ + invalid: {\ + backgroundColor: "red",\ + not: {\ + backgroundColor: "yellow",\ + },\ + },\ + }),\ + componentStateStyles(\ + {\ + invalid: {\ + backgroundColor: "red",\ + },\ + },\ + { parentSelector: "[data-theme=dark]" },\ + ),\ +]); +``` + +``` +Copyts +// styles.css +import { componentStateStyles } from "@kobalte/vanilla-extract"; +import { style } from "@vanilla-extract/css"; + +const button = style([\ + {\ + background: "blue",\ + padding: "2px 6px",\ + },\ + componentStateStyles({\ + disabled: {\ + opacity: 0.4,\ + },\ + invalid: {\ + backgroundColor: "red",\ + not: {\ + backgroundColor: "yellow",\ + },\ + },\ + }),\ + componentStateStyles(\ + {\ + invalid: {\ + backgroundColor: "red",\ + },\ + },\ + { parentSelector: "[data-theme=dark]" },\ + ),\ +]); +``` + +Then apply your styles to the component: + +``` +Copytsx +import { Button } from "@kobalte/core/button"; +import { button } from "./styles.css"; + +export const MyButton = () => ; +``` + +``` +Copytsx +import { Button } from "@kobalte/core/button"; +import { button } from "./styles.css"; + +export const MyButton = () => ; +``` + +## Usage with UnoCSS + +The [UnoCSS preset](https://github.com/zirbest/unocss-preset-primitives#kobalte) made by the community can be used to achieve the same behavior of the TailwindCSS plugin. + +## Extending a component + +Extending a component is done the same way you extend any SolidJS component. + +``` +Copytsx +import { Popover as KPopover } from "@kobalte/core/popover"; +import { ComponentProps } from "solid-js"; + +export const PopoverTrigger = (props: ComponentProps) => { + return ; +}; +``` + +``` +Copytsx +import { Popover as KPopover } from "@kobalte/core/popover"; +import { ComponentProps } from "solid-js"; + +export const PopoverTrigger = (props: ComponentProps) => { + return ; +}; +``` + +Previous[←Getting started](https://kobalte.dev/docs/core/overview/getting-started)Next[Animation→](https://kobalte.dev/docs/core/overview/animation) \ No newline at end of file diff --git a/.firecrawl/logrocket-ai-shadcn.md b/.firecrawl/logrocket-ai-shadcn.md new file mode 100644 index 0000000..f46651f --- /dev/null +++ b/.firecrawl/logrocket-ai-shadcn.md @@ -0,0 +1,381 @@ +[**Advisory boards aren’t only for executives. Join the LogRocket Content Advisory Board today →**](https://lp.logrocket.com/blg/content-advisory-board-signup) + +[![LogRocket blog logo](https://blog.logrocket.com/wp-content/themes/logrocket/assets/logrocket-logo.png)](https://logrocket.com/) + +2025-10-03 + +1610 + +#ai + +Chizaram Ken + +207953 + +102 + +![](https://blog.logrocket.com/wp-content/uploads/2023/04/logrocket-logo-1.png) + +## See how LogRocket's Galileo AI surfaces the most severe issues for you + +### No signup required + +Check it out + +Galileo AI Overview - May 2025 + + +![Video Thumbnail](https://embed-ssl.wistia.com/deliveries/d13588ad6864cb4841845467c9b8feb8.webp?image_crop_resized=1920x1079) + +1:15 + +Click for sound + +You ask Claude Code or Cursor about a shadcn/ui component, and it’ll confidently spit out props that don’t exist, dust off patterns from 2023, or just flat-out make things up. + +![I Tried Shadcn CLI 3.0 — Here’s What I Learned](https://blog.logrocket.com/wp-content/uploads/2025/10/ShadCN.png) + +Most of the time, this comes down to version changes. shadcn/ui keeps evolving, new props, updated requirements, and agents often lean on older docs or outdated patterns. + +Other times, it’s simply the AI guessing. This is the not-so-smart side of AI: it won’t admit “I don’t know,” so it stitches together whatever scraps it half-remembers from training instead of the actual component code. + +Case in point: your agent might suggest ` + + + + + {({ close }) => ( + <> + + Confirm Deletion + + +

+ Are you sure you want to permanently delete this file? +

+ +
+ + + +
+ + )} +
+
+
+ + ); +} + +export default AriaDialog; +``` + +Now, add styling. React Aria provides built-in class names such as `.react-aria-Button`, which you can use directly in CSS. You can also override them with custom classes like `.btn` in this example: + +```css +/* aria.style.css */ + +.btn { + padding: 0.5rem 1.2rem; + border-radius: 0.2rem; + border: none; + cursor: pointer; +} + +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.red-btn { + background-color: #d32f2f; + color: #ffffff; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.react-aria-ModalOverlay { + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + animation: overlayAnimation 200ms cubic-bezier(0.19, 1, 0.22, 1); + display: flex; + justify-content: center; + align-items: center; +} + +.react-aria-Dialog { + background-color: white; + border-radius: 0.2rem; + width: 90vw; + max-width: 450px; + padding: 2.5rem; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, + rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; + outline: none; +} + +.react-aria-Dialog .react-aria-Heading { + font-size: 1.1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #dfdddd; + margin-bottom: 1rem; +} + +.dialog-body { + margin-bottom: 3rem; +} + +.bottom-btns { + display: flex; + justify-content: flex-end; +} + +.bottom-btns .btn:last-child { + display: inline-block; + margin-left: 1rem; +} + +@keyframes overlayAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +Finally, export and render the component in the DOM. + +Here is the output of the dialog box in this example: + +![Dialog component built using React Aria styled with custom CSS](https://blog.logrocket.com/wp-content/uploads/2024/06/dialog-box-react-aria.gif)Dialog component built using React Aria styled with custom CSS + +### React Aria pros and cons + +**Pros** + +- It offers hooks for individual components, which support incremental adoption. +- It provides 43 main components. +- All components include built-in class names, simplifying styling. + +**Cons** + +- Some components require more setup. For example, the dialog required destructuring the `close` function and explicitly wiring it to buttons. +- Components often need to be combined to function fully. In this example, we used `Button`, `Dialog`, `DialogTrigger`, `Heading`, `Modal`, and `ModalOverlay` together to build a dialog. This structure can feel complex at first. + +## Ark UI + +[Ark UI](https://ark-ui.com/) is a library of unstyled components that work across React, Vue, and Solid. It is developed by Chakra Systems, the team behind Chakra UI. The project has gained steady adoption, with around 4.9K stars on [GitHub](https://github.com/chakra-ui/ark) and thousands of weekly npm downloads. + +Like Radix Primitives and React Aria, Ark UI allows you to style headless components using any method you prefer, including CSS, Tailwind CSS, Panda CSS, or Styled Components. One of its distinguishing features is multi-framework support. + +### Installing and using Ark UI + +In this example, we’ll build another dialog box using Ark UI and style it with vanilla CSS. + +First, create a new React project or open an existing one. Then install Ark UI for React: + +```bash +npm install @ark-ui/react +``` + +Next, import and use the unstyled components. Below is the anatomy of a dialog in Ark UI: + +```javascript +// ArkDialog.jsx + +import { Dialog, Portal } from '@ark-ui/react'; +import './ark.style.css'; + +function ArkDialog() { + return ( + + + Ark UI Dialog + + + + + + + + + Confirm Deletion + + + + Are you sure you want to permanently delete this file? + + +
+ + Cancel + + + + Delete Forever + +
+
+
+
+
+ ); +} + +export default ArkDialog; +``` + +Now, style the component using your preferred method. Here is a vanilla CSS example: + +```css +/* ark.style.css */ + +.btn { + padding: 0.5rem 1.2rem; + border-radius: 0.2rem; + border: none; + cursor: pointer; +} + +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.red-btn { + background-color: #d32f2f; + color: #ffffff; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +[data-scope="dialog"][data-part="backdrop"] { + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + animation: backdropAnimation 200ms cubic-bezier(0.19, 1, 0.22, 1); +} + +[data-scope="dialog"][data-part="positioner"] { + position: fixed; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 90vw; + max-width: 450px; +} + +[data-scope="dialog"][data-part="content"] { + background-color: white; + padding: 2.5rem; + border-radius: 0.2rem; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, + rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; +} + +[data-scope="dialog"][data-part="title"] { + font-size: 1.1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #dfdddd; + margin-bottom: 1rem; +} + +[data-scope="dialog"][data-part="description"] { + margin-bottom: 3rem; +} + +.bottom-btns { + display: flex; + justify-content: flex-end; +} + +.bottom-btns .btn:last-child { + display: inline-block; + margin-left: 1rem; +} + +@keyframes backdropAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +Finally, export and render the component. Below is the output of the example: + +![Dialog component built using Ark UI styled with custom CSS](https://blog.logrocket.com/wp-content/uploads/2024/06/dialog-box-ark-ui.gif)Dialog component built using Ark UI styled with custom CSS + +### Ark UI pros and cons + +**Pros** + +- It provides 34 main components. +- It includes advanced components such as a carousel and circular progress bar, which can be complex to implement from scratch. +- It supports [component composition](https://ark-ui.com/react/docs/guides/composition) using the `asChild` prop, similar to Radix Primitives. + +**Cons** + +- It does not provide built-in class names like React Aria. +- The recommended styling approach relies on `data-scope` and `data-part` attributes, which may feel unfamiliar at first. + +For example, styling a specific part of the dialog can look like this: + +```css +[data-scope="dialog"][data-part="positioner"] { + position: fixed; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 90vw; + max-width: 450px; +} +``` + +Developers who prefer a more familiar workflow can assign custom class names using `className` and target those instead: + +```css +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} +``` + +This approach preserves Ark UI’s headless behavior while allowing conventional CSS styling. + +## Base UI + +[Base UI](https://base-ui.com/) is a library of unstyled React components built by contributors from Radix, Floating UI, and the Material UI team. While it follows the same headless philosophy as the other libraries discussed in this article, Base UI places a stronger emphasis on stable APIs that are well-suited for building long-term custom design systems. At the time of writing, Base UI has more than 8.1K stars on its [GitHub repository](https://github.com/mui/base-ui) and is actively maintained with regular releases. + +Like the other headless libraries in this guide, Base UI components can be styled using CSS, Tailwind CSS, or CSS-in-JS. The documentation also includes guidance on advanced patterns such as controlled dialogs and detached triggers. + +### Installing and using Base UI + +Unlike Radix Primitives, which publishes each component separately, Base UI ships all components in a single tree-shakable package. This makes installation straightforward. + +First, create a new React project or open an existing one. Then install Base UI: + +```bash +npm i @base-ui/react +``` + +Next, create a file and import the `Dialog` component. In this example, we’ll build another dialog box: + +```javascript +// BaseDialog.jsx + +import { Dialog } from '@base-ui/react/dialog'; +import './base.style.css'; + +function BaseDialog() { + return ( + + + Base UI Dialog + + + + + + + + Confirm Deletion + + + + Are you sure you want to permanently delete this file? + + +
+ + Cancel + + + + Delete Forever + +
+
+
+
+ ); +} + +export default BaseDialog; +``` + +Now, add styling: + +```css +/* base.style.css */ + +.btn { + padding: 0.5rem 1.2rem; + border-radius: 0.2rem; + border: none; + cursor: pointer; +} + +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.red-btn { + background-color: #d32f2f; + color: #ffffff; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.dialog-overlay { + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + animation: overlayAnimation 200ms cubic-bezier(0.19, 1, 0.22, 1); +} + +.dialog-content { + background-color: white; + position: fixed; + border-radius: 0.2rem; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 90vw; + max-width: 450px; + padding: 2.5rem; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, + rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; +} + +.dialog-title { + font-size: 1.1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #dfdddd; + margin-bottom: 1rem; +} + +.dialog-body { + margin-bottom: 3rem; +} + +.bottom-btns { + display: flex; + justify-content: flex-end; +} + +.bottom-btns .btn:last-child { + display: inline-block; + margin-left: 1rem; +} + +@keyframes overlayAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +Finally, import and render the component in your application: + +* * * + +The Best of React on PodRocket - Emily Kochanek \| Spotify + +[Play on Spotify](https://open.spotify.com/playlist/4j70ZVQNj5Lk3gtyCG5VWk?go=1&sp_cid=9b33d2eacd138b2cca505c1298fc4c90&utm_source=embed_player_p&utm_medium=desktop "Play on Spotify") + +# [The Best of React on PodRocket](https://open.spotify.com/playlist/4j70ZVQNj5Lk3gtyCG5VWk?go=1&sp_cid=9b33d2eacd138b2cca505c1298fc4c90&utm_source=embed_player_p&utm_medium=desktop) + +PreviewE + +## [Emily Kochanek](https://open.spotify.com/playlist/4j70ZVQNj5Lk3gtyCG5VWk?go=1&sp_cid=9b33d2eacd138b2cca505c1298fc4c90&utm_source=embed_player_p&utm_medium=desktop) + +Save on Spotify + +01. 1 + + + ### The React Core team talks React Forget and the future + + + + #### PodRocket + + + 40:49 + +02. 2 + + + ### Why I still choose React with Chance Strickland + + + + #### PodRocket + + + 36:03 + +03. 3 + + + ### The future of React with Theo Browne + + + + #### PodRocket + + + 31:32 + +04. 4 + + + ### Should you use React in 2023? with Tru Narla + + + + #### PodRocket + + + 27:01 + +05. 5 + + + ### Your React questions answered + + + + #### PodRocket + + + 39:01 + +06. 6 + + + ### React and frontend development with Josh Comeau + + + + #### PodRocket + + + 23:32 + +07. 7 + + + ### Tom Preston-Werner talks React Server Components and the future of RedwoodJS + + + + #### PodRocket + + + 40:19 + +08. 8 + + + ### How React 18 improves application performance with Lydia Hallie + + + + #### PodRocket + + + 27:14 + +09. 9 + + + ### SolidJS for React developers with Erik Rasmussen + + + + #### PodRocket + + + 36:59 + +10. 10 + + + ### Understanding React's Fiber architecture with Tejas Kumar + + + + #### PodRocket + + + 25:01 + +11. 11 + + + ### React concurrency with Ivan Akulov + + + + #### PodRocket + + + 35:24 + +12. 12 + + + ### Next-generation React architectures with Mikael Brevik + + + + #### PodRocket + + + 38:52 + +13. 13 + + + ### React Server Components with Sanket Sahu + + + + #### PodRocket + + + 25:17 + +14. 14 + + + ### Stack Overflow survey, React Server Components, and Google Domains + + + + #### PodRocket + + + 37:20 + +15. 15 + + + ### Speed up React apps with less JavaScript with Miško Hevery + + + + #### PodRocket + + + 24:56 + +16. 16 + + + ### React, TypeScript, and ESLint with Josh Goldberg + + + + #### PodRocket + + + 30:10 + +17. 17 + + + ### Node 20, React, and AI with Theo Browne, Michael Chan, and Eve Porcello + + + + #### PodRocket + + + 44:29 + +18. 18 + + + ### React's re-rendering with Ankita Kulkarni + + + + #### PodRocket + + + 28:45 + +19. 19 + + + ### React Hooks broke my tests with Daniel Afonso + + + + #### PodRocket + + + 31:30 + +20. 20 + + + ### Deep diving on concurrent React with Matheus Albuquerque + + + + #### PodRocket + + + 38:07 + +21. 21 + + + ### React Compiler with Sathya Gunasekaran and Joe Savona + + + + #### PodRocket + + + 25:43 + +22. 22 + + + ### 50 shades of React rendering in Next.js with Ben Ilegbodu + + + + #### PodRocket + + + 27:04 + +23. 23 + + + ### Mid-year recap, Vercel Ship, and remixing React Router + + + + #### PodRocket + + + 39:40 + +24. 24 + + + ### Visualizing Open Source Data in React with Brian Douglas + + + + #### PodRocket + + + 36:08 + +25. 25 + + + ### Incomplete React with Sam Selikoff + + + + #### PodRocket + + + 32:12 + +26. 26 + + + ### React Components as a service with Steven Fabre + + + + #### PodRocket + + + 26:35 + +27. 27 + + + ### Partial pre-rendering in Next.js with Delba de Oliveira and Wyatt Johnson + + + + #### PodRocket + + + 26:23 + +28. 28 + + + ### Using RSCs in Expo Router with Evan Bacon + + + + #### PodRocket + + + 33:11 + +29. 29 + + + ### Generative UI and React components with Malte Ubl + + + + #### PodRocket + + + 24:57 + +30. 30 + + + ### Web without walls with Dan Abramov + + + + #### PodRocket + + + 27:40 + +31. 31 + + + ### React 19 with Shruti Kapoor + + + + #### PodRocket + + + 30:06 + +32. 32 + + + ### Universal React with Mo Khazali + + + + #### PodRocket + + + 36:09 + +33. 33 + + + ### An App Developer's Guide to React 19 with Michael Chan + + + + #### PodRocket + + + 26:01 + +34. 34 + + + ### Component composition with Dominik Dorfmiester + + + + #### PodRocket + + + 19:03 + + +- [Play on Spotify](https://open.spotify.com/playlist/4j70ZVQNj5Lk3gtyCG5VWk?go=1&sp_cid=9b33d2eacd138b2cca505c1298fc4c90&utm_source=embed_player_p&utm_medium=desktop) +- Save on Spotify +- Copy link + +[Privacy Policy](https://www.spotify.com/legal/privacy-policy/)· [Terms & Conditions](https://www.spotify.com/legal) + +* * * + +```javascript +import './App.css'; +import BaseDialog from './BaseDialog'; + +function App() { + return ( + <> + + + ); +} + +export default App; +``` + +And you should see output similar to the example below: + +![Dialog component built using Base UI styled with custom CSS](https://paper-attachments.dropboxusercontent.com/s_08FF8C07E650BFE785F4BD3E05EABB4D3111B626CF5B80B239A8E948CFAEF6ED_1768982503267_Jan-21-2026+08-51-42.gif)Dialog component built using Base UI styled with custom CSS + +### Base UI pros and cons + +**Pros** + +- It ships as a single tree-shakable package, eliminating the need to install components individually. +- It includes strong documentation and supports advanced patterns such as controlled dialogs and detached triggers. + +**Cons** + +- Its ecosystem is still growing compared to more established alternatives. +- Because it is unstyled by design, significant styling work is still required to align it with a production design system. + +## Comparing the headless component libraries + +To provide a clearer overview of how these headless UI libraries compare across API design, styling flexibility, composition model, and intended use cases, the table below highlights the key differences between Radix Primitives, React Aria, Ark UI, and Base UI. + +| Dimension | Radix Primitives | React Aria | Ark UI | Base UI | +| --- | --- | --- | --- | --- | +| **Primary goal** | Polished primitives for app UIs | Accessibility-first primitives | Cross-framework state-driven primitives | Foundation for custom design systems | +| **Mental model** | Component anatomy and composition | Hooks with explicit state | State machines and parts | Low-level primitives meant to be wrapped | +| **Typical usage** | Used directly in application code | Composed per component | Assembled from parts | Extended into internal components | +| **Styling approach** | `className`, `asChild` | Built-in classes with overrides | `data-part` / `data-scope` with `className` | `className` and wrapper components | +| **Ease of styling** | Easy and familiar | Easy once conventions are understood | Moderate, unconventional at first | Easy, but assumes design ownership | +| **Composition flexibility** | High | Very high | High | Very high | +| **Accessibility transparency** | Mostly abstracted | Very explicit | Abstracted via state | Abstracted but predictable | +| **Learning curve** | Moderate | Steep | Moderate to steep | Moderate | +| **Best suited for** | Product teams building applications | Accessibility-critical applications | Multi-framework design systems | Teams building custom design systems | +| **Framework support** | React | React | React, Vue, Solid | React | + +This comparison demonstrates that while these libraries often provide similar component coverage, they differ significantly in how components are composed, styled, and extended. + +Choosing the right headless UI library ultimately depends on your project goals, team preferences, and long-term maintenance strategy. The following quick guide can help narrow down your options: + +- **Use Radix Primitives** if you want mature, well-documented components that can be used directly in application code with minimal setup. +- **Use React Aria** if accessibility is a primary concern and you prefer explicit, hook-based control over component behavior. +- **Use Ark UI** if you need headless components that work across multiple frameworks such as React, Vue, and Solid. +- **Use Base UI** if you are building a custom design system and want a flexible, long-term foundation for your own components. + +The best choice depends less on feature parity and more on how well a library’s design philosophy aligns with your team’s workflow and architectural goals. + +## Conclusion + +This guide explored why developers may look beyond Tailwind Labs’ Headless UI library when choosing unstyled component libraries. We examined several strong alternatives, including Radix Primitives, React Aria, Ark UI, and Base UI. + +The frontend ecosystem continues to adopt headless UI libraries because many teams want more control over how components behave and how they are styled. Having multiple headless options available is beneficial, as different projects have different architectural and design needs. + +## Get set up with LogRocket's modern React error tracking in minutes: + +1. Visit [https://logrocket.com/signup/](https://lp.logrocket.com/blg/react-signup-general) to get + an app ID + +2. Install LogRocket via npm or script tag. `LogRocket.init()` must be called client-side, not + server-side + + + + + - [npm](https://blog.logrocket.com/headless-ui-alternatives/#plug-tab-1) + - [Script tag](https://blog.logrocket.com/headless-ui-alternatives/#plug-tab-2) + +``` +$ npm i --save logrocket + +// Code: + +import LogRocket from 'logrocket'; +LogRocket.init('app/id'); + +``` + +``` +// Add to your HTML: + + + + +``` + +3. (Optional) Install plugins for deeper integrations with your stack: + - Redux middleware + + - NgRx middleware + + - Vuex plugin + +[Get started now](https://lp.logrocket.com/blg/react-signup-general) + +- [#react](https://blog.logrocket.com/tag/react/) + +![](https://blog.logrocket.com/wp-content/uploads/2022/06/footer-cta-dots-left.png)![](https://blog.logrocket.com/wp-content/uploads/2022/06/footer-cta-dots-right.png) + +![](https://blog.logrocket.com/wp-content/uploads/2022/09/logrocket-logo-frontend-analytics.png) + +## Stop guessing about your digital experience with LogRocket + +[Get started for free](https://lp.logrocket.com/blg/signup) + +#### Recent posts: + +[![Crud Rest Api With Node Js Express And Postgresql](https://blog.logrocket.com/wp-content/uploads/2022/05/CRUD-REST-API-Node-js-Express-PostgreSQL.png)\\ +**CRUD REST API with Node.js, Express, and PostgreSQL**](https://blog.logrocket.com/crud-rest-api-node-js-express-postgresql/) + +Build a CRUD REST API with Node.js, Express, and PostgreSQL, then modernize it with ES modules, async/await, built-in Express middleware, and safer config handling. + +[![](https://blog.logrocket.com/wp-content/uploads/2019/04/taniarascia-150x150.png)](https://blog.logrocket.com/author/taniarascia/)[Tania Rascia](https://blog.logrocket.com/author/taniarascia/) + +Mar 25, 2026 ⋅ 16 min read + +[![the replay march 25](https://blog.logrocket.com/wp-content/uploads/2026/03/The-Replay-Graphic-21.png)\\ +**The Replay (3/25/26): Senior dev hiring woes, what AI agents miss, and more**](https://blog.logrocket.com/the-replay-3-25-26/) + +Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the March 25th issue. + +[![](https://secure.gravatar.com/avatar/bdb8fc1c58c8a14e8c52f80c0ff964372237e773db8d0d1a179151099f28c358?s=50&d=mm&r=g)](https://blog.logrocket.com/author/matthewmaccormack/)[Matt MacCormack](https://blog.logrocket.com/author/matthewmaccormack/) + +Mar 25, 2026 ⋅ 29 sec read + +[![emmanuel john senior dev hiring](https://blog.logrocket.com/wp-content/uploads/2026/03/3.25-Emmanuel-John-Senior-Engineers-1.png)\\ +**The hidden skills gap in senior dev hiring (and how to screen for it)**](https://blog.logrocket.com/the-hidden-skills-gap-in-senior-dev-hiring/) + +Discover a practical framework for redesigning your senior developer hiring process to screen for real diagnostic skill. + +[![](https://blog.logrocket.com/wp-content/uploads/2021/01/AirBrush_20210107121828-150x150.jpg)](https://blog.logrocket.com/author/emmanueljohn/)[Emmanuel John](https://blog.logrocket.com/author/emmanueljohn/) + +Mar 25, 2026 ⋅ 12 min read + +[![](https://blog.logrocket.com/wp-content/uploads/2026/03/Does-the-Speculation-Rules-API-boost-web-speed-I-tested-it.png)\\ +**Does the Speculation Rules API boost web speed? I tested it**](https://blog.logrocket.com/speculation-rules-api-web-speed-test/) + +I tested the Speculation Rules API in a real project to see if it actually improves navigation speed. Here’s what worked, what didn’t, and where it’s worth using. + +[![](https://blog.logrocket.com/wp-content/uploads/2025/01/IMG_1212-150x150.jpg)](https://blog.logrocket.com/author/judemiracle/)[Jude Miracle](https://blog.logrocket.com/author/judemiracle/) + +Mar 24, 2026 ⋅ 10 min read + +[View all posts](https://blog.logrocket.com/) + +#### 2 Replies to "Headless UI alternatives: Radix Primitives vs. React Aria vs. Ark UI vs. Base UI" + +1. \> such as Base UI (from the Material UI team) + + + +This is confusing. The link points to “MUI Base”. But this project has a successor now: Base UI, [https://base-ui.com/](https://base-ui.com/). + + + + + +[Reply](https://blog.logrocket.com/headless-ui-alternatives/#comment-51239) + +1. Thanks for noticing this. Should be all set now + + + + + + [Reply](https://blog.logrocket.com/headless-ui-alternatives/#comment-51257) + +### Leave a Reply [Cancel reply](https://blog.logrocket.com/headless-ui-alternatives/\#respond) + +Your email address will not be published.Required fields are marked \* + +Comment \* + +Name \* + +Email \* + +Website + +Save my name, email, and website in this browser for the next time I comment. + +Hey there, want to help make our blog better? + +YeaNo Thanks + +Join LogRocket’s Content Advisory Board. You’ll help inform the type of +content we create and get access to exclusive meetups, social accreditation, +and swag. + + +[Sign up now](https://lp.logrocket.com/blg/content-advisory-board-signup) \ No newline at end of file diff --git a/.firecrawl/logrocket-headless-ui.md b/.firecrawl/logrocket-headless-ui.md new file mode 100644 index 0000000..6dd9933 --- /dev/null +++ b/.firecrawl/logrocket-headless-ui.md @@ -0,0 +1,934 @@ +[**Advisory boards aren’t only for executives. Join the LogRocket Content Advisory Board today →**](https://lp.logrocket.com/blg/content-advisory-board-signup) + +[![LogRocket blog logo](https://blog.logrocket.com/wp-content/themes/logrocket/assets/logrocket-logo.png)](https://logrocket.com/) + +2026-03-02 + +3006 + +#react + +Amazing Enyichi Agu + +192451 + +116 + +![](https://blog.logrocket.com/wp-content/uploads/2023/04/logrocket-logo-1.png) + +## See how LogRocket's Galileo AI surfaces the most severe issues for you + +### No signup required + +Check it out + +Galileo AI Overview - May 2025 + + +![Video Thumbnail](https://embed-ssl.wistia.com/deliveries/d13588ad6864cb4841845467c9b8feb8.webp?image_crop_resized=1920x1079) + +1:15 + +Click for sound + +_**Editor’s note:** This post was updated in March 2026 by [Elijah Asoula](https://blog.logrocket.com/author/asaoluelijah/) to include Base UI and add updated examples and use cases to make the comparison more actionable._ + +![Headless UI Alternatives: Radix Primitives, React Aria, Ark UI](https://blog.logrocket.com/wp-content/uploads/2024/06/headless-ui-alternatives-radix-primitives-react-aria-ark-ui.png) + +Using React component libraries is a popular way to quickly build React applications. Components from these libraries offer several advantages. First, they follow accessibility guidelines such as [WAI-ARIA](https://www.w3.org/WAI/standards-guidelines/aria/), ensuring that applications are usable by everyone. Second, they come with built-in styling and design so developers can focus on other aspects of their applications. Third, many include pre-defined behaviors — for example, an autocomplete component that filters options based on user input — which saves time and effort compared to building from scratch. + +React component libraries are also typically optimized for performance. Because they are maintained by large communities or organizations, they receive regular updates and follow efficient coding practices. Examples include [Material UI](https://blog.logrocket.com/guide-material-design-react/), [Chakra UI](https://blog.logrocket.com/chakra-ui-adoption-guide/), and [React Bootstrap](https://www.youtube.com/watch?v=NlZUtfNVAkI). + +However, these libraries leave limited room for customization. You can usually tweak styles, but you cannot fundamentally change the underlying design system. A developer may want the accessibility and functionality benefits of a component library while still implementing a completely custom design system. + +Headless (unstyled) component libraries were created to fill this gap. A headless component library provides fully functional components without imposing styling. With headless components, developers are responsible for styling them however they see fit. + +Tailwind Labs’ [Headless UI](https://headlessui.com/) is one of the most popular headless libraries in the React ecosystem. While it works well for many projects, it is not always the best choice for every use case. This article explores several alternatives for unstyled components, including Radix Primitives, React Aria, Ark UI, and Base UI. + +### 🚀 Sign up for The Replay newsletter + +[**The Replay**](https://blog.logrocket.com/the-replay-archive/) is a weekly newsletter for dev and engineering leaders. + +Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software. + +Fields marked with an \* are required + +Email \* + +If you are a human seeing this field, please leave it empty. + + +## Prerequisites + +To follow along with this guide, you should have a basic understanding of HTML, CSS, JavaScript, and React. + +## Why not just use Tailwind Labs’ Headless UI library? + +Headless UI is an unstyled React component library developed by Tailwind Labs, the creators of Tailwind CSS. The library is designed to integrate particularly well with Tailwind CSS, as noted in its documentation. It is also one of the most widely adopted headless libraries, with around 28K GitHub stars and millions of weekly npm downloads. + +However, Headless UI is limited in the number of unstyled components it provides. At the time of writing, it offers 16 primary components. The other libraries covered in this article provide significantly more components for broader use cases. Additionally, some of these alternatives include utility components and helper functions that Headless UI does not offer. + +Let’s explore these alternatives. + +## Radix Primitives + +[Radix Primitives](https://www.radix-ui.com/primitives) is a library of unstyled React components built by the team behind [Radix UI](https://radix-ui.com/), a UI library with fully styled and customizable components. According to its website, the Node.js, Vercel, and Supabase teams use Radix Primitives. The project has approximately 18K stars on [GitHub](https://github.com/radix-ui/primitives). + +You can [style Radix Primitives components](https://blog.logrocket.com/radix-ui-adoption-guide/#:~:text=you%20should%20know.-,Radix%20Primitives,-Radix%20Primitives%20is) using any styling solution, including CSS, Tailwind CSS, or CSS-in-JS. The components also support server-side rendering. Radix provides comprehensive documentation for each primitive, explaining usage patterns and composition strategies. + +### Installing and using Radix Primitives + +The following steps demonstrate how to install and use Radix Primitives. In this example, we’ll import a dialog component and style it using vanilla CSS. + +First, [create a React project](https://react.dev/learn/creating-a-react-app) using your preferred framework, or open an existing project. + +Next, install the Radix primitive you need. Radix publishes each component as a separate package. For this example, install the `Dialog` component: + +```bash +npm install @radix-ui/react-dialog +``` + +Now, create a file to import and customize the unstyled component: + +```javascript +// RadixDialog.jsx + +import * as Dialog from '@radix-ui/react-dialog'; +import './radix.style.css'; + +function RadixDialog() { + return ( + + + Radix Dialog + + + + + + + + Confirm Deletion + + + + Are you sure you want to permanently delete this file? + + +
+ Cancel + Delete Forever +
+
+
+
+ ); +} + +export default RadixDialog; +``` + +Next, add styling: + +```css +/* radix.style.css */ + +.btn { + padding: 0.5rem 1.2rem; + border-radius: 0.2rem; + border: none; + cursor: pointer; +} + +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.red-btn { + background-color: #d32f2f; + color: #ffffff; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.dialog-overlay { + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + animation: overlayAnimation 200ms cubic-bezier(0.19, 1, 0.22, 1); +} + +.dialog-content { + background-color: white; + position: fixed; + border-radius: 0.2rem; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 90vw; + max-width: 450px; + padding: 2.5rem; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, + rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; +} + +.dialog-title { + font-size: 1.1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #dfdddd; + margin-bottom: 1rem; +} + +.dialog-body { + margin-bottom: 3rem; +} + +.bottom-btns { + display: flex; + justify-content: flex-end; +} + +.bottom-btns .btn:last-child { + display: inline-block; + margin-left: 1rem; +} + +@keyframes overlayAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +Finally, export and render the component in the DOM. + +Here is the UI demo of the dialog component we styled above: + +![Dialog box built using Radix Primitives styled with custom CSS](https://blog.logrocket.com/wp-content/uploads/2024/06/dialog-box-radix-primitive.gif)Dialog box built using Radix Primitives styled with custom CSS + +### Radix Primitives pros and cons + +Like every headless library covered in this guide, Radix Primitives has both advantages and tradeoffs. + +**Pros** + +* * * + +[![](https://blog.logrocket.com/wp-content/uploads/2023/07/Screen-Shot-2023-07-06-at-7.43.53-AM.png)\\ +\\ +**Over 200k developers use LogRocket to create better digital experiences** \\ +\\ +![](https://blog.logrocket.com/wp-content/uploads/2022/08/rocket-button-icon.png)Learn more →](https://lp.logrocket.com/blg/learn-more) + +* * * + +- It offers 28 main components, significantly more than Headless UI. +- You can install components individually, allowing incremental adoption. +- It provides an `asChild` prop that lets developers change the default DOM element of a Radix component — a pattern known as [composition](https://www.radix-ui.com/primitives/docs/guides/composition). + +**Cons** + +- Installing multiple components individually can feel repetitive. +- The anatomy-based structure of components can take time to understand. + +## React Aria + +[React Aria](https://react-spectrum.adobe.com/react-aria/index.html) is a library of unstyled components released by Adobe as part of its React UI collection, [React Spectrum](https://github.com/adobe/react-spectrum). While Adobe does not maintain a separate repository exclusively for React Aria, the React Spectrum repository has over 14K GitHub stars at the time of writing. Its npm package, `react-aria-components`, receives thousands of weekly downloads. + +React Aria allows developers to style components using any preferred styling method. It also supports incremental adoption through [React Aria hooks](https://react-spectrum.adobe.com/react-aria/hooks.html), enabling fine-grained control over component behavior. + +### Installing and using React Aria + +In this example, we’ll build another dialog box using React Aria, styled similarly to the Radix example. + +First, create a new React application or open an existing project. Then install the component package: + +```bash +npm install react-aria-components +``` + +Next, import the required components to construct a dialog: + +```javascript +// AriaDialog.jsx + +import { + Button, + Dialog, + DialogTrigger, + Heading, + Modal, + ModalOverlay +} from 'react-aria-components'; + +import './aria.style.css'; + +function AriaDialog() { + return ( + + + + + + + {({ close }) => ( + <> + + Confirm Deletion + + +

+ Are you sure you want to permanently delete this file? +

+ +
+ + + +
+ + )} +
+
+
+
+ ); +} + +export default AriaDialog; +``` + +Now, add styling. React Aria provides built-in class names such as `.react-aria-Button`, which you can use directly in CSS. You can also override them with custom classes like `.btn` in this example: + +```css +/* aria.style.css */ + +.btn { + padding: 0.5rem 1.2rem; + border-radius: 0.2rem; + border: none; + cursor: pointer; +} + +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.red-btn { + background-color: #d32f2f; + color: #ffffff; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.react-aria-ModalOverlay { + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + animation: overlayAnimation 200ms cubic-bezier(0.19, 1, 0.22, 1); + display: flex; + justify-content: center; + align-items: center; +} + +.react-aria-Dialog { + background-color: white; + border-radius: 0.2rem; + width: 90vw; + max-width: 450px; + padding: 2.5rem; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, + rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; + outline: none; +} + +.react-aria-Dialog .react-aria-Heading { + font-size: 1.1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #dfdddd; + margin-bottom: 1rem; +} + +.dialog-body { + margin-bottom: 3rem; +} + +.bottom-btns { + display: flex; + justify-content: flex-end; +} + +.bottom-btns .btn:last-child { + display: inline-block; + margin-left: 1rem; +} + +@keyframes overlayAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +Finally, export and render the component in the DOM. + +Here is the output of the dialog box in this example: + +![Dialog component built using React Aria styled with custom CSS](https://blog.logrocket.com/wp-content/uploads/2024/06/dialog-box-react-aria.gif)Dialog component built using React Aria styled with custom CSS + +### React Aria pros and cons + +**Pros** + +- It offers hooks for individual components, which support incremental adoption. +- It provides 43 main components. +- All components include built-in class names, simplifying styling. + +**Cons** + +- Some components require more setup. For example, the dialog required destructuring the `close` function and explicitly wiring it to buttons. +- Components often need to be combined to function fully. In this example, we used `Button`, `Dialog`, `DialogTrigger`, `Heading`, `Modal`, and `ModalOverlay` together to build a dialog. This structure can feel complex at first. + +## Ark UI + +[Ark UI](https://ark-ui.com/) is a library of unstyled components that work across React, Vue, and Solid. It is developed by Chakra Systems, the team behind Chakra UI. The project has gained steady adoption, with around 4.9K stars on [GitHub](https://github.com/chakra-ui/ark) and thousands of weekly npm downloads. + +Like Radix Primitives and React Aria, Ark UI allows you to style headless components using any method you prefer, including CSS, Tailwind CSS, Panda CSS, or Styled Components. One of its distinguishing features is multi-framework support. + +### Installing and using Ark UI + +In this example, we’ll build another dialog box using Ark UI and style it with vanilla CSS. + +First, create a new React project or open an existing one. Then install Ark UI for React: + +```bash +npm install @ark-ui/react +``` + +Next, import and use the unstyled components. Below is the anatomy of a dialog in Ark UI: + +```javascript +// ArkDialog.jsx + +import { Dialog, Portal } from '@ark-ui/react'; +import './ark.style.css'; + +function ArkDialog() { + return ( + + + Ark UI Dialog + + + + + + + + + Confirm Deletion + + + + Are you sure you want to permanently delete this file? + + +
+ + Cancel + + + + Delete Forever + +
+
+
+
+
+ ); +} + +export default ArkDialog; +``` + +Now, style the component using your preferred method. Here is a vanilla CSS example: + +```css +/* ark.style.css */ + +.btn { + padding: 0.5rem 1.2rem; + border-radius: 0.2rem; + border: none; + cursor: pointer; +} + +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.red-btn { + background-color: #d32f2f; + color: #ffffff; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +[data-scope="dialog"][data-part="backdrop"] { + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + animation: backdropAnimation 200ms cubic-bezier(0.19, 1, 0.22, 1); +} + +[data-scope="dialog"][data-part="positioner"] { + position: fixed; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 90vw; + max-width: 450px; +} + +[data-scope="dialog"][data-part="content"] { + background-color: white; + padding: 2.5rem; + border-radius: 0.2rem; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, + rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; +} + +[data-scope="dialog"][data-part="title"] { + font-size: 1.1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #dfdddd; + margin-bottom: 1rem; +} + +[data-scope="dialog"][data-part="description"] { + margin-bottom: 3rem; +} + +.bottom-btns { + display: flex; + justify-content: flex-end; +} + +.bottom-btns .btn:last-child { + display: inline-block; + margin-left: 1rem; +} + +@keyframes backdropAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +Finally, export and render the component. Below is the output of the example: + +![Dialog component built using Ark UI styled with custom CSS](https://blog.logrocket.com/wp-content/uploads/2024/06/dialog-box-ark-ui.gif)Dialog component built using Ark UI styled with custom CSS + +### Ark UI pros and cons + +**Pros** + +- It provides 34 main components. +- It includes advanced components such as a carousel and circular progress bar, which can be complex to implement from scratch. +- It supports [component composition](https://ark-ui.com/react/docs/guides/composition) using the `asChild` prop, similar to Radix Primitives. + +**Cons** + +- It does not provide built-in class names like React Aria. +- The recommended styling approach relies on `data-scope` and `data-part` attributes, which may feel unfamiliar at first. + +For example, styling a specific part of the dialog can look like this: + +```css +[data-scope="dialog"][data-part="positioner"] { + position: fixed; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 90vw; + max-width: 450px; +} +``` + +Developers who prefer a more familiar workflow can assign custom class names using `className` and target those instead: + +```css +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} +``` + +This approach preserves Ark UI’s headless behavior while allowing conventional CSS styling. + +## Base UI + +[Base UI](https://base-ui.com/) is a library of unstyled React components built by contributors from Radix, Floating UI, and the Material UI team. While it follows the same headless philosophy as the other libraries discussed in this article, Base UI places a stronger emphasis on stable APIs that are well-suited for building long-term custom design systems. At the time of writing, Base UI has more than 8.1K stars on its [GitHub repository](https://github.com/mui/base-ui) and is actively maintained with regular releases. + +Like the other headless libraries in this guide, Base UI components can be styled using CSS, Tailwind CSS, or CSS-in-JS. The documentation also includes guidance on advanced patterns such as controlled dialogs and detached triggers. + +### Installing and using Base UI + +Unlike Radix Primitives, which publishes each component separately, Base UI ships all components in a single tree-shakable package. This makes installation straightforward. + +First, create a new React project or open an existing one. Then install Base UI: + +```bash +npm i @base-ui/react +``` + +Next, create a file and import the `Dialog` component. In this example, we’ll build another dialog box: + +```javascript +// BaseDialog.jsx + +import { Dialog } from '@base-ui/react/dialog'; +import './base.style.css'; + +function BaseDialog() { + return ( + + + Base UI Dialog + + + + + + + + Confirm Deletion + + + + Are you sure you want to permanently delete this file? + + +
+ + Cancel + + + + Delete Forever + +
+
+
+
+ ); +} + +export default BaseDialog; +``` + +Now, add styling: + +```css +/* base.style.css */ + +.btn { + padding: 0.5rem 1.2rem; + border-radius: 0.2rem; + border: none; + cursor: pointer; +} + +.primary-btn { + background-color: #1e64e7; + color: white; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.red-btn { + background-color: #d32f2f; + color: #ffffff; + box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 10px; +} + +.dialog-overlay { + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + inset: 0; + animation: overlayAnimation 200ms cubic-bezier(0.19, 1, 0.22, 1); +} + +.dialog-content { + background-color: white; + position: fixed; + border-radius: 0.2rem; + top: 50%; + left: 50%; + translate: -50% -50%; + width: 90vw; + max-width: 450px; + padding: 2.5rem; + box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, + rgba(0, 0, 0, 0.3) 0px 1px 3px -1px; +} + +.dialog-title { + font-size: 1.1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #dfdddd; + margin-bottom: 1rem; +} + +.dialog-body { + margin-bottom: 3rem; +} + +.bottom-btns { + display: flex; + justify-content: flex-end; +} + +.bottom-btns .btn:last-child { + display: inline-block; + margin-left: 1rem; +} + +@keyframes overlayAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +``` + +Finally, import and render the component in your application: + +* * * + +* * * + +```javascript +import './App.css'; +import BaseDialog from './BaseDialog'; + +function App() { + return ( + <> + + + ); +} + +export default App; +``` + +And you should see output similar to the example below: + +![Dialog component built using Base UI styled with custom CSS](https://paper-attachments.dropboxusercontent.com/s_08FF8C07E650BFE785F4BD3E05EABB4D3111B626CF5B80B239A8E948CFAEF6ED_1768982503267_Jan-21-2026+08-51-42.gif)Dialog component built using Base UI styled with custom CSS + +### Base UI pros and cons + +**Pros** + +- It ships as a single tree-shakable package, eliminating the need to install components individually. +- It includes strong documentation and supports advanced patterns such as controlled dialogs and detached triggers. + +**Cons** + +- Its ecosystem is still growing compared to more established alternatives. +- Because it is unstyled by design, significant styling work is still required to align it with a production design system. + +## Comparing the headless component libraries + +To provide a clearer overview of how these headless UI libraries compare across API design, styling flexibility, composition model, and intended use cases, the table below highlights the key differences between Radix Primitives, React Aria, Ark UI, and Base UI. + +| Dimension | Radix Primitives | React Aria | Ark UI | Base UI | +| --- | --- | --- | --- | --- | +| **Primary goal** | Polished primitives for app UIs | Accessibility-first primitives | Cross-framework state-driven primitives | Foundation for custom design systems | +| **Mental model** | Component anatomy and composition | Hooks with explicit state | State machines and parts | Low-level primitives meant to be wrapped | +| **Typical usage** | Used directly in application code | Composed per component | Assembled from parts | Extended into internal components | +| **Styling approach** | `className`, `asChild` | Built-in classes with overrides | `data-part` / `data-scope` with `className` | `className` and wrapper components | +| **Ease of styling** | Easy and familiar | Easy once conventions are understood | Moderate, unconventional at first | Easy, but assumes design ownership | +| **Composition flexibility** | High | Very high | High | Very high | +| **Accessibility transparency** | Mostly abstracted | Very explicit | Abstracted via state | Abstracted but predictable | +| **Learning curve** | Moderate | Steep | Moderate to steep | Moderate | +| **Best suited for** | Product teams building applications | Accessibility-critical applications | Multi-framework design systems | Teams building custom design systems | +| **Framework support** | React | React | React, Vue, Solid | React | + +This comparison demonstrates that while these libraries often provide similar component coverage, they differ significantly in how components are composed, styled, and extended. + +Choosing the right headless UI library ultimately depends on your project goals, team preferences, and long-term maintenance strategy. The following quick guide can help narrow down your options: + +- **Use Radix Primitives** if you want mature, well-documented components that can be used directly in application code with minimal setup. +- **Use React Aria** if accessibility is a primary concern and you prefer explicit, hook-based control over component behavior. +- **Use Ark UI** if you need headless components that work across multiple frameworks such as React, Vue, and Solid. +- **Use Base UI** if you are building a custom design system and want a flexible, long-term foundation for your own components. + +The best choice depends less on feature parity and more on how well a library’s design philosophy aligns with your team’s workflow and architectural goals. + +## Conclusion + +This guide explored why developers may look beyond Tailwind Labs’ Headless UI library when choosing unstyled component libraries. We examined several strong alternatives, including Radix Primitives, React Aria, Ark UI, and Base UI. + +The frontend ecosystem continues to adopt headless UI libraries because many teams want more control over how components behave and how they are styled. Having multiple headless options available is beneficial, as different projects have different architectural and design needs. + +## Get set up with LogRocket's modern React error tracking in minutes: + +1. Visit [https://logrocket.com/signup/](https://lp.logrocket.com/blg/react-signup-general) to get + an app ID + +2. Install LogRocket via npm or script tag. `LogRocket.init()` must be called client-side, not + server-side + + + + + - [npm](https://blog.logrocket.com/headless-ui-alternatives/#plug-tab-1) + - [Script tag](https://blog.logrocket.com/headless-ui-alternatives/#plug-tab-2) + +``` +$ npm i --save logrocket + +// Code: + +import LogRocket from 'logrocket'; +LogRocket.init('app/id'); + +``` + +``` +// Add to your HTML: + + + + +``` + +3. (Optional) Install plugins for deeper integrations with your stack: + - Redux middleware + + - NgRx middleware + + - Vuex plugin + +[Get started now](https://lp.logrocket.com/blg/react-signup-general) + +- [#react](https://blog.logrocket.com/tag/react/) + +![](https://blog.logrocket.com/wp-content/uploads/2022/06/footer-cta-dots-left.png)![](https://blog.logrocket.com/wp-content/uploads/2022/06/footer-cta-dots-right.png) + +![](https://blog.logrocket.com/wp-content/uploads/2022/09/logrocket-logo-frontend-analytics.png) + +## Stop guessing about your digital experience with LogRocket + +[Get started for free](https://lp.logrocket.com/blg/signup) + +#### Recent posts: + +[![Crud Rest Api With Node Js Express And Postgresql](https://blog.logrocket.com/wp-content/uploads/2022/05/CRUD-REST-API-Node-js-Express-PostgreSQL.png)\\ +**CRUD REST API with Node.js, Express, and PostgreSQL**](https://blog.logrocket.com/crud-rest-api-node-js-express-postgresql/) + +Build a CRUD REST API with Node.js, Express, and PostgreSQL, then modernize it with ES modules, async/await, built-in Express middleware, and safer config handling. + +[![](https://blog.logrocket.com/wp-content/uploads/2019/04/taniarascia-150x150.png)](https://blog.logrocket.com/author/taniarascia/)[Tania Rascia](https://blog.logrocket.com/author/taniarascia/) + +Mar 25, 2026 ⋅ 16 min read + +[![the replay march 25](https://blog.logrocket.com/wp-content/uploads/2026/03/The-Replay-Graphic-21.png)\\ +**The Replay (3/25/26): Senior dev hiring woes, what AI agents miss, and more**](https://blog.logrocket.com/the-replay-3-25-26/) + +Discover what’s new in The Replay, LogRocket’s newsletter for dev and engineering leaders, in the March 25th issue. + +[![](https://secure.gravatar.com/avatar/bdb8fc1c58c8a14e8c52f80c0ff964372237e773db8d0d1a179151099f28c358?s=50&d=mm&r=g)](https://blog.logrocket.com/author/matthewmaccormack/)[Matt MacCormack](https://blog.logrocket.com/author/matthewmaccormack/) + +Mar 25, 2026 ⋅ 29 sec read + +[![emmanuel john senior dev hiring](https://blog.logrocket.com/wp-content/uploads/2026/03/3.25-Emmanuel-John-Senior-Engineers-1.png)\\ +**The hidden skills gap in senior dev hiring (and how to screen for it)**](https://blog.logrocket.com/the-hidden-skills-gap-in-senior-dev-hiring/) + +Discover a practical framework for redesigning your senior developer hiring process to screen for real diagnostic skill. + +[![](https://blog.logrocket.com/wp-content/uploads/2021/01/AirBrush_20210107121828-150x150.jpg)](https://blog.logrocket.com/author/emmanueljohn/)[Emmanuel John](https://blog.logrocket.com/author/emmanueljohn/) + +Mar 25, 2026 ⋅ 12 min read + +[![](https://blog.logrocket.com/wp-content/uploads/2026/03/Does-the-Speculation-Rules-API-boost-web-speed-I-tested-it.png)\\ +**Does the Speculation Rules API boost web speed? I tested it**](https://blog.logrocket.com/speculation-rules-api-web-speed-test/) + +I tested the Speculation Rules API in a real project to see if it actually improves navigation speed. Here’s what worked, what didn’t, and where it’s worth using. + +[![](https://blog.logrocket.com/wp-content/uploads/2025/01/IMG_1212-150x150.jpg)](https://blog.logrocket.com/author/judemiracle/)[Jude Miracle](https://blog.logrocket.com/author/judemiracle/) + +Mar 24, 2026 ⋅ 10 min read + +[View all posts](https://blog.logrocket.com/) + +#### 2 Replies to "Headless UI alternatives: Radix Primitives vs. React Aria vs. Ark UI vs. Base UI" + +1. \> such as Base UI (from the Material UI team) + + + +This is confusing. The link points to “MUI Base”. But this project has a successor now: Base UI, [https://base-ui.com/](https://base-ui.com/). + + + + + +[Reply](https://blog.logrocket.com/headless-ui-alternatives/#comment-51239) + +1. Thanks for noticing this. Should be all set now + + + + + + [Reply](https://blog.logrocket.com/headless-ui-alternatives/#comment-51257) + +### Leave a Reply [Cancel reply](https://blog.logrocket.com/headless-ui-alternatives/\#respond) + +Your email address will not be published.Required fields are marked \* + +Comment \* + +Name \* + +Email \* + +Website + +Save my name, email, and website in this browser for the next time I comment. + +Hey there, want to help make our blog better? + +YeaNo Thanks + +Join LogRocket’s Content Advisory Board. You’ll help inform the type of +content we create and get access to exclusive meetups, social accreditation, +and swag. + + +[Sign up now](https://lp.logrocket.com/blg/content-advisory-board-signup) \ No newline at end of file diff --git a/.firecrawl/openui-overview.md b/.firecrawl/openui-overview.md new file mode 100644 index 0000000..18cf272 --- /dev/null +++ b/.firecrawl/openui-overview.md @@ -0,0 +1,130 @@ +# Overview + +Key building blocks of the OpenUI framework and the built-in component libraries. + +Copy MarkdownOpen + +OpenUI is built around four core building blocks that work together to turn LLM output into rendered UI: + +- **Library** — A collection of components defined with Zod schemas and React renderers. The library is the contract between your app and the AI — it defines what components the LLM can use and how they render. + +- **Prompt Generator** — Converts your library into a system prompt that instructs the LLM to output valid OpenUI Lang. Includes syntax rules, component signatures, streaming guidelines, and your custom examples/rules. + +- **Parser** — Parses OpenUI Lang text (line-by-line, streaming-compatible) into a typed element tree. Validates against your library's JSON Schema and gracefully handles partial/invalid output. + +- **Renderer** — The `` React component takes parsed output and maps each element to your library's React components, rendering the UI progressively as the stream arrives. + + +## [Built-in Component Libraries](https://www.openui.com/docs/openui-lang/overview\#built-in-component-libraries) + +OpenUI ships with two ready-to-use libraries via `@openuidev/react-ui`. Both include layouts, content blocks, charts, forms, tables, and more. + +### [General-purpose library (`openuiLibrary`)](https://www.openui.com/docs/openui-lang/overview\#general-purpose-library-openuilibrary) + +Root component is `Stack`. Includes the full component suite with flexible layout primitives. Use this for standalone rendering, playgrounds, and non-chat interfaces. + +``` +import { openuiLibrary, openuiPromptOptions } from "@openuidev/react-ui/genui-lib"; +import { Renderer } from "@openuidev/react-lang"; + +// Generate system prompt +const systemPrompt = openuiLibrary.prompt(openuiPromptOptions); + +// Render streamed output + +``` + +### [Chat-optimized library (`openuiChatLibrary`)](https://www.openui.com/docs/openui-lang/overview\#chat-optimized-library-openuichatlibrary) + +Root component is `Card` (vertical container, no layout params). Adds chat-specific components like `FollowUpBlock`, `ListBlock`, and `SectionBlock`. Does not include `Stack` — responses are always single-card, vertically stacked. + +``` +import { openuiChatLibrary, openuiChatPromptOptions } from "@openuidev/react-ui/genui-lib"; +import { FullScreen } from "@openuidev/react-ui"; + +// Use with a chat layout + +``` + +Both libraries expose a `.prompt()` method to generate the system prompt your LLM needs. See [System Prompts](https://www.openui.com/docs/openui-lang/system-prompts) for CLI and programmatic generation options. + +### [Extend a built-in library](https://www.openui.com/docs/openui-lang/overview\#extend-a-built-in-library) + +``` +import { createLibrary, defineComponent } from "@openuidev/react-lang"; +import { openuiLibrary } from "@openuidev/react-ui/genui-lib"; +import { z } from "zod"; + +const ProductCard = defineComponent({ + name: "ProductCard", + description: "Product tile", + props: z.object({ + name: z.string(), + price: z.number(), + }), + component: ({ props }) =>
{props.name}: ${props.price}
, +}); + +const myLibrary = createLibrary({ + root: openuiLibrary.root ?? "Stack", + componentGroups: openuiLibrary.componentGroups, + components: [...Object.values(openuiLibrary.components), ProductCard], +}); +``` + +## [Usage Example](https://www.openui.com/docs/openui-lang/overview\#usage-example) + +Define LibRender CodeSystem PromptLLM Output + +OpenUI Lang (Token Efficient)Copy + +``` +root = Stack([welcomeCard]) +welcomeCard = MyCard([welcomeHeader, welcomeBody]) +welcomeHeader = CardHeader("Welcome", "Get started with our platform") +welcomeBody = Stack([signupForm], "column", "m") +signupForm = Form("signup", [nameField, emailField], actions) +nameField = FormControl("Name", Input("name", "Your name", "text", ["required", "minLength:2"])) +emailField = FormControl("Email", Input("email", "you@example.com", "email", ["required", "email"])) +actions = Buttons([signUpBtn, learnMoreBtn], "row") +signUpBtn = Button("Sign up", "submit:signup", "primary") +learnMoreBtn = Button("Learn more", "action:learn_more", "secondary") +``` + +Output Preview + +Welcome + +Get started with our platform + +Name\* + +Email\* + +Sign upLearn more + +## [Next Steps](https://www.openui.com/docs/openui-lang/overview\#next-steps) + +[**Defining Components** \\ +\\ +Create custom components with Zod schemas and React renderers.](https://www.openui.com/docs/openui-lang/defining-components) [**System Prompts** \\ +\\ +Generate and customize LLM instructions from your library.](https://www.openui.com/docs/openui-lang/system-prompts) [**The Renderer** \\ +\\ +Parse and render streamed OpenUI Lang in React.](https://www.openui.com/docs/openui-lang/renderer) [**Chat Integration** \\ +\\ +Build AI chat interfaces with prebuilt layouts.](https://www.openui.com/docs/chat) + +[Quick Start\\ +\\ +Bootstrap a Generative UI chat app in under a minute.](https://www.openui.com/docs/openui-lang/quickstart) [Defining Components\\ +\\ +Define OpenUI components with Zod and React renderers.](https://www.openui.com/docs/openui-lang/defining-components) + +### On this page + +[Built-in Component Libraries](https://www.openui.com/docs/openui-lang/overview#built-in-component-libraries) [General-purpose library (`openuiLibrary`)](https://www.openui.com/docs/openui-lang/overview#general-purpose-library-openuilibrary) [Chat-optimized library (`openuiChatLibrary`)](https://www.openui.com/docs/openui-lang/overview#chat-optimized-library-openuichatlibrary) [Extend a built-in library](https://www.openui.com/docs/openui-lang/overview#extend-a-built-in-library) [Usage Example](https://www.openui.com/docs/openui-lang/overview#usage-example) [Next Steps](https://www.openui.com/docs/openui-lang/overview#next-steps) \ No newline at end of file diff --git a/.firecrawl/openui-spec.md b/.firecrawl/openui-spec.md new file mode 100644 index 0000000..1e41214 --- /dev/null +++ b/.firecrawl/openui-spec.md @@ -0,0 +1,289 @@ +# OpenUI Specification + +draft-01 + +## 1\. Introduction + +**OpenUI** is a specification format for defining User Interface (UI) components in an abstract, implementation-agnostic manner. Inspired by specifications like **OpenAPI**, OpenUI describes UI components, their properties and behaviors in a way that can be used across various UI libraries and frameworks. This approach provides a standardized, machine-readable and human-readable model for UI libraries, facilitating a consistent means of documentation, testing and code generation. + +OpenUI is designed to be **AI-native**, making it easier for AI tools and assistants to parse, understand and leverage information about your UI components or design system. By offering a uniform description of components, OpenUI aids in bridging the gap between diverse frameworks, ensuring interoperability and reducing fragmentation. + +## 2\. Specification Interpretation (RFC 2119) + +This specification uses key terms from [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119): + +- **MUST** indicates an absolute requirement. +- **SHOULD** indicates a recommendation that, while not strictly required, carries significant weight if not implemented. +- **MAY** indicates an option or possibility left to implementer discretion. + +These terms are used carefully within this document to clarify how certain aspects of OpenUI **MUST**, **SHOULD**, or **MAY** be interpreted or implemented. + +## 3\. Purpose and Goals + +OpenUI addresses the growing need for a common language to describe UI components. It streamlines collaboration among designers, developers, technical writers and AI-based systems. Its key goals are: + +1. **Standardizing UI Component Libraries** + + +Provide a consistent schema for describing components and design systems. + +2. **Interoperability Across Frameworks** + + +Abstract away implementation details, enabling easier migration or shared usage across React, Vue, Angular and other libraries. + +3. **AI-Native** + + +Supply structured data optimized for consumption by AI tools, improving automated documentation, validation and code generation. + +4. **Efficiency** + + +Reduce duplication and confusion by having a single, authoritative reference that clarifies how components behave and interact. + +5. **Built-In Accessibility and Validation** + + +Promote best practices for accessible design while enabling validation and testing tools to verify component conformance. + +6. **Support for Web and Native Platforms** + + +Offer a universal approach that can adapt to both web-based and native environments. + +7. **Documentation + Testing + Code Generation** + + +Enable a broad ecosystem of tooling, from auto-generated docs to integrated testing frameworks. + + +## 4\. Overview and Key Features + +### AI-Native Specification + +OpenUI's structured format is particularly well-suited for AI-based analysis and tooling. By adhering to a consistent schema, developers can integrate OpenUI with LLM-based assistants or other AI tools to: + +- Generate automated tests and documentation. +- Provide contextual suggestions during development. +- Perform higher-level reasoning about component usage. + +### Standardization of UI Components + +OpenUI makes it straightforward to define each component's shape, props, events and usage guidelines. This standardization can reduce the learning curve for new developers and makes it easier to share components across teams or projects. + +### Efficiency and Universal Application + +By referencing a single specification file, organizations can ensure consistent APIs and behaviors across multiple implementations. OpenUI also supports various popular UI libraries and frameworks out of the box. + +## 5\. Similarities and Differences with Other Tools + +1. **Type Definition Files** + - **Similarities**: Both OpenUI and typical type definition files (e.g., TypeScript `.d.ts`) can define types, enumerations and interfaces in a way that tools can parse. + - **Differences**: + + - Type definitions often focus on compile-time checks and may include internal or unrelated types across multiple files, without extensive usage or behavioral documentation. + - OpenUI, by contrast, is designed to be more concise and self-contained. It places all relevant component information (including usage examples, accessibility considerations and enumerated props) into a single, portable file. This provides a broader but more focused view of each component’s intended purpose and functionality. +2. `**llms.txt**` + - **Similarities**: Can also include structured text or partial metadata for Large Language Models (LLMs). + - **Differences**: `llms.txt` often contains entire documentation sets or large text blocks, making them unwieldy for direct AI consumption without retrieval augmented generation (RAG). OpenUI, by contrast, is deliberately minimal and structured specifically for UI component definitions, easing both human and machine comprehension. +3. **Storybook** + - **Similarities**: Provides extensive documentation for UI components and their usage patterns. + - **Differences**: Storybook offers live previews and interactive exploration, while OpenUI is a static, implementation-agnostic specification. +4. **OpenAPI** + - **Similarities**: Both define a domain (OpenAPI for HTTP APIs, OpenUI for UI components) using a schema that is machine-readable and human-readable. + - **Differences**: OpenAPI is dedicated to endpoints and data payloads, whereas OpenUI focuses on user interface components and their properties. + +OpenUI **complements** these existing tools by providing a formal specification for UI components, bridging the gap between full interactive environments (e.g., Storybook) and purely type-focused definitions (e.g., TypeScript `.d.ts` files). + +## 6\. Why OpenUI? + +1. **Consistency** + + +Streamlines how UI components are documented, reducing confusion among teams and projects. + +2. **Framework Agnosticism** + + +Fits multiple frameworks (e.g., React, Vue, Angular) by focusing on essential component definitions rather than implementation details. + +3. **Standardization** + + +Encourages best practices for design systems and shared UI libraries. + +4. **Enhanced Developer Experience** + + +Simplifies onboarding and collaboration, as all information about a component is located in a centralized specification. + +5. **AI Readiness** + + +Supplies metadata in a format that AI tools can easily parse, leading to improved automated code insights, error detection and overall development efficiency. + + +## 7\. Specification Format + +OpenUI specifications **MUST** be defined in **YAML** or **JSON**. Below is the general structure required at the top level: + +| Property | Type | Description | +| --- | --- | --- | +| **name** | string | Name of the UI library or component set. | +| **version** | string | Version of the UI library or component set. | +| **description** | string | Brief overview of the UI library or component set. | +| **components** | object | Collection of components in the library. | + +Each entry in `components` represents a **component**, defined by: + +| Property | Type | Description | +| --- | --- | --- | +| **package** | string (opt.) | (Optional) Package or module where the component is located. | +| **description** | string | Component overview. | +| **example** | string (opt.) | (Optional) Example usage of the component. | +| **props** | object | Map of properties supported by the component. | + +Within `props`, each **prop** can be a string indicating the type or an object containing: + +| Property | Type | Description | +| --- | --- | --- | +| **type** | string | Data type for the prop (e.g., `string`, `boolean`, `number`). | +| **description** | string | Explanation of the prop's purpose and usage. | +| **default** | any | Default value for the prop, if applicable. | +| **enum** | array | List of allowable values (if the prop is an enumerated type). | +| **required** | boolean | Whether the prop is mandatory (e.g., `true` means it MUST be provided). | + +## 8\. Filename and Discovery + +The OpenUI specification file **MUST** be named **`openui.yaml`** or **`openui.json`**. It **SHOULD** reside at the **root** of your GitHub repository or the base path of your website. This approach makes it straightforward for both humans and automated tools to locate and parse the specification. + +## 9\. Example Specification + +```yaml +name: Example UI Library +version: 1.0.0 +description: A sample UI library specification + +components: + Button: + description: A clickable button element + example: | + + props: + variant: + type: string + description: The visual style of the button. + enum: + - primary + - secondary + - outline + size: + type: string + description: The size of the button. + enum: + - small + - medium + - large + disabled: + type: boolean + description: Disables the button if set to true. + default: false +``` + +In this example: + +- **`variant`** is a string-based enumerated prop. +- **`size`** is another enumerated prop. +- **`disabled`** is a boolean with a default value. + +## 10\. Best Practices + +1. **Reflect the Design System Accurately** + + +All components, props and enumerations **SHOULD** accurately match the functionality and constraints in the actual UI library or design system. + +2. **Document Events** + + +If a component emits events (e.g., `onClick`), these **SHOULD** be included to ensure completeness. + +3. **Emphasize Accessibility** + + +Integrate relevant accessibility attributes or guidelines within your component definitions to promote inclusive design. + +4. **Maintain Consistency** + + +Use a uniform style for naming and describing components and props across the specification. + +5. **Keep the Specification Updated** + + +Update the specification whenever the codebase evolves to avoid discrepancies. + + +## 11\. Future Plans + +OpenUI is an evolving specification. Planned or potential enhancements include: + +1. **Describing Component State and Lifecycle** + + +Support for advanced UI patterns, including transitions and complex state management. + +2. **Tooling for Code Generation** + + +Official plugins or libraries to automatically generate documentation, tests and skeleton code. + +3. **Library and Framework Integrations** + + +Core support for popular frameworks such as React, Vue, Angular and others. + +4. **Complex UI Patterns** + + +Handling dynamic scenarios such as multi-step forms, modals, or asynchronous data loading. + + +## 12\. Contributing + +OpenUI is an open-source project and contributions are welcomed. If you wish to propose changes or add new features: + +1. **Fork the Repository** + + +Create a personal fork of the OpenUI repository. + +2. **Create a Branch** + + +Use a descriptive name (e.g., `feature/add-modal-spec`). + +3. **Implement Changes** + + +Follow the established style and schema guidelines when modifying or adding files. + +4. **Validate** + + +Confirm your updates are syntactically correct and conform to the schema requirements. + +5. **Submit a Pull Request** + + +Propose your changes for review by the maintainers. + + +For more information, refer to the [OpenUI repository](https://github.com/ctate/openui). + +## 13\. License + +The OpenUI specification is distributed under the **MIT License** \ No newline at end of file diff --git a/.firecrawl/pkgpulse-bundling.md b/.firecrawl/pkgpulse-bundling.md new file mode 100644 index 0000000..968cf90 --- /dev/null +++ b/.firecrawl/pkgpulse-bundling.md @@ -0,0 +1,361 @@ +[Skip to main content](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026#main-content) + +## [TL;DR](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tldr) + +**tsup** is the most popular TypeScript library bundler — zero-config, generates ESM + CJS + `.d.ts` types automatically, used by thousands of npm packages. **tsdown** is the next-generation successor to tsup — built on Rolldown (Vite's Rust-based bundler), significantly faster, same DX but better performance. **unbuild** is from the UnJS ecosystem — supports multiple build presets, stub mode for development (no build step), and is used by Nuxt, Nitro, and UnJS libraries internally. In 2026: tsup is the safe choice with the largest community, tsdown is the emerging performance leader, unbuild if you're in the Nuxt/UnJS ecosystem. + +## [Key Takeaways](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#key-takeaways) + +- **tsup**: ~6M weekly downloads — esbuild-based, zero-config, ESM + CJS + types in one command +- **tsdown**: ~500K weekly downloads — Rolldown-based (Rust), 3-5x faster than tsup, tsup-compatible API +- **unbuild**: ~3M weekly downloads — UnJS, stub mode, multiple presets, Rollup-based +- All three generate dual ESM/CJS output — required for modern npm packages +- All three generate TypeScript declaration files (`.d.ts`) automatically +- tsdown is rapidly gaining adoption in 2026 as the fast tsup replacement + +* * * + +## [Why Library Bundling Matters](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#why-library-bundling-matters) + +``` +Problem: publishing TypeScript to npm requires: + 1. Compile TypeScript → JavaScript + 2. Generate .d.ts type declarations + 3. Create both ESM (import) and CJS (require) versions + 4. Tree-shaking to minimize bundle size + 5. Correct package.json "exports" map + +Without a bundler: + tsc --outDir dist → CJS only, no bundling + + manually maintain package.json exports + + separately configure dts generation + + no tree-shaking + +With tsup/tsdown/unbuild: + One command → dist/index.mjs + dist/index.cjs + dist/index.d.ts +``` + +* * * + +## [tsup](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsup) + +[tsup](https://tsup.egoist.dev/) — zero-config library bundler: + +### [Setup](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#setup) + +```bash +npm install -D tsup typescript + +# Add to package.json scripts: +{ + "scripts": { + "build": "tsup", + "dev": "tsup --watch" + } +} +``` + +### [tsup.config.ts](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsupconfigts) + +```typescript +import { defineConfig } from "tsup" + +export default defineConfig({ + entry: ["src/index.ts"], // Entry point(s) + format: ["esm", "cjs"], // Output ESM + CommonJS + dts: true, // Generate .d.ts files + splitting: false, // Code splitting (for multiple entry points) + sourcemap: true, // Generate source maps + clean: true, // Clean dist/ before build + minify: false, // Don't minify libraries (let consumers decide) + external: ["react", "vue"], // Don't bundle peer dependencies + treeshake: true, // Remove unused code + target: "es2020", // Output target + outDir: "dist", +}) +``` + +### [Multiple entry points](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#multiple-entry-points) + +```typescript +import { defineConfig } from "tsup" + +export default defineConfig({ + // Multiple entry points (for sub-path exports): + entry: { + index: "src/index.ts", + server: "src/server.ts", + client: "src/client.ts", + }, + format: ["esm", "cjs"], + dts: true, + splitting: true, // Share code between entry points +}) + +// Generates: +// dist/index.mjs + dist/index.js + dist/index.d.ts +// dist/server.mjs + dist/server.js + dist/server.d.ts +// dist/client.mjs + dist/client.js + dist/client.d.ts +``` + +### [package.json for dual ESM/CJS](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#packagejson-for-dual-esmcjs) + +```json +{ + "name": "my-library", + "version": "1.0.0", + "main": "./dist/index.js", // CJS entry (legacy) + "module": "./dist/index.mjs", // ESM entry (bundlers) + "types": "./dist/index.d.ts", // TypeScript types + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "./server": { + "import": "./dist/server.mjs", + "require": "./dist/server.js" + } + }, + "files": ["dist"], + "scripts": { + "build": "tsup", + "prepublishOnly": "npm run build" + }, + "devDependencies": { + "tsup": "^8.0.0", + "typescript": "^5.0.0" + } +} +``` + +### [Watch mode for development](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#watch-mode-for-development) + +```bash +# Rebuild on file changes: +tsup --watch + +# Or in parallel with your dev server: +# package.json: +{ + "scripts": { + "dev": "concurrently \"tsup --watch\" \"node dist/index.js\"" + } +} +``` + +* * * + +## [tsdown](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsdown) + +[tsdown](https://tsdown.egoist.dev/) — the Rolldown-based tsup successor: + +### [Why tsdown is faster](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#why-tsdown-is-faster) + +``` +tsup uses: esbuild (Go) → fast, but JS orchestration overhead +tsdown uses: Rolldown (Rust) → faster bundler + faster orchestration + +Build time comparison (real-world library with 50 files): + tsup: ~2.5s + tsdown: ~0.6s + (varies by project size and machine) +``` + +### [Setup (nearly identical to tsup)](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#setup-nearly-identical-to-tsup) + +```typescript +// tsdown.config.ts +import { defineConfig } from "tsdown" + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm", "cjs"], + dts: true, + clean: true, + sourcemap: true, + external: ["react"], +}) +``` + +```bash +# Commands are the same as tsup: +npx tsdown # Build +npx tsdown --watch # Watch mode +``` + +### [tsup → tsdown migration](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsup--tsdown-migration) + +```bash +# Install: +npm uninstall tsup +npm install -D tsdown + +# Rename config file: +mv tsup.config.ts tsdown.config.ts + +# Update import: +# - import { defineConfig } from "tsup" +# + import { defineConfig } from "tsdown" + +# Update package.json scripts: +# - "build": "tsup" +# + "build": "tsdown" +``` + +* * * + +## [unbuild](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#unbuild) + +[unbuild](https://github.com/unjs/unbuild) — UnJS library bundler: + +### [Setup](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#setup-1) + +```typescript +// build.config.ts +import { defineBuildConfig } from "unbuild" + +export default defineBuildConfig({ + entries: ["src/index"], + rollup: { + emitCJS: true, // Also emit CommonJS + }, + declaration: true, // Generate .d.ts + clean: true, +}) +``` + +```bash +# Build: +npx unbuild + +# Stub mode (development): +npx unbuild --stub +``` + +### [Stub mode (unique to unbuild)](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#stub-mode-unique-to-unbuild) + +```typescript +// "Stub mode" — generates proxy files that require/import the source directly +// No build step needed during development! + +// dist/index.mjs (stub): +// export * from "../src/index.ts" + +// dist/index.js (stub): +// module.exports = require("../src/index.ts") // via jiti + +// Benefits: +// - No watch mode needed — file changes are picked up immediately +// - Faster feedback loop when developing a library locally +// - Link the package to a consumer with npm link — changes are live + +// Production build: +// npx unbuild ← produces real bundles (no stub) +``` + +### [Multiple presets](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#multiple-presets) + +```typescript +import { defineBuildConfig } from "unbuild" + +export default defineBuildConfig([\ + // Main package:\ + {\ + entries: ["src/index"],\ + declaration: true,\ + rollup: { emitCJS: true },\ + },\ + // CLI entry (no types needed):\ + {\ + entries: [{ input: "src/cli", name: "cli" }],\ + declaration: false,\ + rollup: {\ + emitCJS: false,\ + inlineDependencies: true, // Bundle everything into the CLI binary\ + },\ + },\ +]) +``` + +### [Used by the UnJS ecosystem](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#used-by-the-unjs-ecosystem) + +``` +unbuild is used by: + - nuxt → @nuxt/... packages + - nitro → the Nuxt server engine + - h3 → the HTTP framework + - ofetch → the fetch wrapper + - Most @unjs/* packages + +If you're contributing to or building in this ecosystem, unbuild +is the natural choice. +``` + +* * * + +## [Feature Comparison](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#feature-comparison) + +| Feature | tsup | tsdown | unbuild | +| --- | --- | --- | --- | +| Build engine | esbuild (Go) | Rolldown (Rust) | Rollup (JS) | +| Build speed | Fast | ⚡ Fastest | Moderate | +| ESM + CJS | ✅ | ✅ | ✅ | +| .d.ts generation | ✅ | ✅ | ✅ | +| Stub mode (no build) | ❌ | ❌ | ✅ | +| Code splitting | ✅ | ✅ | ✅ | +| treeshake | ✅ | ✅ | ✅ | +| Plugin ecosystem | esbuild plugins | Rolldown plugins | Rollup plugins | +| TypeScript config | tsup.config.ts | tsdown.config.ts | build.config.ts | +| Community size | ⭐ Large | Growing fast | Medium | +| Weekly downloads | ~6M | ~500K | ~3M | + +* * * + +## [When to Use Each](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#when-to-use-each) + +**Choose tsup if:** + +- The safe, battle-tested choice — most tutorials and examples use it +- Large community, most Stack Overflow answers, most plugins +- Works for 95% of library use cases out of the box + +**Choose tsdown if:** + +- Build speed is a priority (large libraries, frequent CI builds) +- You're migrating from tsup — API is nearly identical +- On the cutting edge of tooling in 2026 + +**Choose unbuild if:** + +- Working in the Nuxt, Nitro, or UnJS ecosystem +- Want stub mode for instant development without watch rebuilds +- Need Rollup-specific plugins not available in esbuild/Rolldown + +**Also consider:** + +- **Vite Library Mode** — for libraries that need Vite plugins (CSS modules, etc.) +- **pkgroll** — minimal bundler for packages with simple needs +- **microbundle** — smaller alternative, but less actively maintained in 2026 + +* * * + +## [Methodology](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#methodology) + +Download data from npm registry (weekly average, February 2026). Feature comparison based on tsup v8.x, tsdown v0.x, and unbuild v2.x. + +_[Compare build tooling and bundler packages on PkgPulse →](https://www.pkgpulse.com/)_ + +## Comments + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/pkgpulse-floating.md b/.firecrawl/pkgpulse-floating.md new file mode 100644 index 0000000..2ab4222 --- /dev/null +++ b/.firecrawl/pkgpulse-floating.md @@ -0,0 +1,705 @@ +[Skip to main content](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026#main-content) + +# [Floating UI vs Tippy.js vs Radix Tooltip: Popover Positioning 2026](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#floating-ui-vs-tippyjs-vs-radix-tooltip-popover-positioning-2026) + +## [TL;DR](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#tldr) + +Building tooltips, popovers, dropdowns, and floating menus correctly is deceptively hard — viewport overflow, collision detection, scroll containers, and keyboard accessibility are all gotchas that custom solutions routinely miss. **Floating UI** (successor to Popper.js from the same authors) is the low-level positioning engine — pure geometry and collision detection, totally unstyled, works with any framework, and is what Radix, Mantine, and many others use internally. **Tippy.js** is the batteries-included tooltip library built on Popper.js — styled out of the box, declarative API, animates, works in vanilla JS and React — but it's showing its age in 2026 with no App Router support and weaker accessibility guarantees. **Radix UI's Tooltip and Popover** are headless, fully accessible (WAI-ARIA compliant), React-only components built on Floating UI internally — the correct choice for React/Next.js component libraries where accessibility is non-negotiable. For low-level control over positioning in any framework: Floating UI. For quick tooltips with minimal config: Tippy.js. For production React UIs that must be accessible: Radix Tooltip/Popover. + +## [Key Takeaways](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#key-takeaways) + +- **Floating UI is framework-agnostic** — core is vanilla JS, `@floating-ui/react` adds React hooks +- **Floating UI handles all edge cases** — viewport overflow, flip, shift, arrow, virtual elements +- **Tippy.js is easiest to get started** — `` wraps any element +- **Radix Tooltip is fully WAI-ARIA compliant** — focus management, screen readers, keyboard nav +- **Tippy.js is built on Popper.js** — Floating UI's predecessor, still maintained but less active +- **Radix Popover manages open state** — controlled and uncontrolled modes, portal rendering +- **Floating UI powers Radix internally** — Radix uses `@floating-ui/react-dom` under the hood + +* * * + +## [Use Case Map](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#use-case-map) + +``` +Simple tooltip on hover → Tippy.js or Radix Tooltip +Tooltip with custom render → Floating UI or Radix Tooltip +Accessible popover with content → Radix Popover +Dropdown menu with keyboard nav → Radix DropdownMenu +Custom positioning engine → Floating UI (raw) +Framework-agnostic tooltip → Tippy.js or Floating UI +Select/Combobox overlay → Floating UI or Radix Select +Context menu (right-click) → Radix ContextMenu +``` + +* * * + +## [Floating UI: The Positioning Engine](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#floating-ui-the-positioning-engine) + +Floating UI provides the geometry and collision detection algorithms — you wire up the DOM refs and React state yourself. + +### [Installation](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#installation) + +```bash +npm install @floating-ui/react +# For vanilla JS (no React): +npm install @floating-ui/dom +``` + +### [Basic Tooltip](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#basic-tooltip) + +```tsx +import { + useFloating, + autoUpdate, + offset, + flip, + shift, + useHover, + useFocus, + useDismiss, + useRole, + useInteractions, + FloatingPortal, +} from "@floating-ui/react"; +import { useState } from "react"; + +interface TooltipProps { + content: string; + children: React.ReactElement; +} + +export function Tooltip({ content, children }: TooltipProps) { + const [isOpen, setIsOpen] = useState(false); + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + placement: "top", + // Keep in sync with scroll and resize + whileElementsMounted: autoUpdate, + middleware: [\ + offset(8), // Distance from reference\ + flip(), // Flip to bottom if no space above\ + shift({ padding: 8 }), // Shift horizontally to stay in viewport\ + ], + }); + + // Interaction hooks — compose behaviors + const hover = useHover(context, { move: false }); + const focus = useFocus(context); + const dismiss = useDismiss(context); + const role = useRole(context, { role: "tooltip" }); + + const { getReferenceProps, getFloatingProps } = useInteractions([\ + hover,\ + focus,\ + dismiss,\ + role,\ + ]); + + return ( + <> + {/* Attach to trigger element */} + {React.cloneElement(children, { + ref: refs.setReference, + ...getReferenceProps(), + })} + + {/* Tooltip — rendered in portal to escape stacking contexts */} + + {isOpen && ( +
+ {content} +
+ )} +
+ + ); +} + +// Usage + + + +``` + +### [Arrow Placement](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#arrow-placement) + +```tsx +import { + useFloating, + arrow, + offset, + flip, + FloatingArrow, +} from "@floating-ui/react"; +import { useRef } from "react"; + +export function TooltipWithArrow({ content, children }: TooltipProps) { + const arrowRef = useRef(null); + + const { refs, floatingStyles, context, middlewareData, placement } = useFloating({ + middleware: [\ + offset(10),\ + flip(),\ + arrow({ element: arrowRef }),\ + ], + }); + + return ( + <> +
{children}
+ +
+ {content} + {/* FloatingArrow renders an SVG arrow positioned correctly */} + +
+ + ); +} +``` + +### [Popover (Click-to-Open)](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#popover-click-to-open) + +```tsx +import { + useFloating, + autoUpdate, + offset, + flip, + shift, + useClick, + useDismiss, + useRole, + useInteractions, + FloatingPortal, + FloatingFocusManager, +} from "@floating-ui/react"; +import { useState } from "react"; + +export function Popover({ trigger, content }: { trigger: React.ReactNode; content: React.ReactNode }) { + const [isOpen, setIsOpen] = useState(false); + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + placement: "bottom-start", + whileElementsMounted: autoUpdate, + middleware: [offset(4), flip(), shift({ padding: 8 })], + }); + + const click = useClick(context); + const dismiss = useDismiss(context); + const role = useRole(context, { role: "dialog" }); + + const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss, role]); + + return ( + <> +
+ {trigger} +
+ + + {isOpen && ( + // FloatingFocusManager traps focus inside the popover + +
+ {content} +
+
+ )} +
+ + ); +} +``` + +### [Virtual Element (Context Menu)](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#virtual-element-context-menu) + +```tsx +import { useFloating, offset, flip, shift, useClientPoint, useInteractions } from "@floating-ui/react"; +import { useState } from "react"; + +export function ContextMenu({ items }: { items: string[] }) { + const [isOpen, setIsOpen] = useState(false); + + const { refs, floatingStyles, context } = useFloating({ + open: isOpen, + onOpenChange: setIsOpen, + placement: "bottom-start", + middleware: [offset({ mainAxis: 5, alignmentAxis: 4 }), flip(), shift()], + }); + + // Follow the mouse cursor + const clientPoint = useClientPoint(context); + const { getReferenceProps, getFloatingProps } = useInteractions([clientPoint]); + + return ( +
{ + e.preventDefault(); + setIsOpen(true); + }} + style={{ minHeight: 200, border: "1px dashed #ccc", padding: 16 }} + {...getReferenceProps()} + > + Right-click anywhere here + + {isOpen && ( +
+ {items.map((item) => ( + + ))} +
+ )} +
+ ); +} +``` + +* * * + +## [Tippy.js: Batteries-Included Tooltips](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#tippyjs-batteries-included-tooltips) + +Tippy.js provides a complete tooltip and popover solution with themes, animations, and a declarative API — minimal configuration required. + +### [Installation](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#installation-1) + +```bash +npm install tippy.js @tippyjs/react +``` + +### [Basic Usage](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#basic-usage) + +```tsx +import Tippy from "@tippyjs/react"; +import "tippy.js/dist/tippy.css"; // Default theme + +export function CopyButton() { + return ( + + + + ); +} +``` + +### [Placement and Options](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#placement-and-options) + +```tsx +import Tippy from "@tippyjs/react"; + +export function FeatureTooltips() { + return ( +
+ + + + + + + + + {/* Delay: 300ms show, 100ms hide */} + + + + + {/* Click to toggle instead of hover */} + + + + + {/* Interactive (won't close when hovering tooltip) */} + + Rich content +

With multiple elements

+ Read more +
+ } + interactive + interactiveBorder={20} + placement="bottom" + > + +
+ + {/* Disabled */} + + + + + + + ); +} +``` + +### [Animations and Themes](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#animations-and-themes) + +```tsx +import Tippy from "@tippyjs/react"; +import "tippy.js/dist/tippy.css"; +import "tippy.js/animations/scale.css"; +import "tippy.js/themes/light.css"; +import "tippy.js/themes/material.css"; + +export function ThemedTooltips() { + return ( + <> + {/* Built-in light theme */} + + + + + {/* Scale animation */} + + + + + {/* Custom theme via CSS */} + + + + + ); +} +``` + +### [Controlled Tippy](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#controlled-tippy) + +```tsx +import Tippy from "@tippyjs/react"; +import { useState } from "react"; + +export function ControlledTooltip() { + const [visible, setVisible] = useState(false); + + return ( + setVisible(false)} + interactive + > + + + ); +} +``` + +* * * + +## [Radix UI Tooltip and Popover: Accessible Components](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#radix-ui-tooltip-and-popover-accessible-components) + +Radix provides fully accessible, headless components with correct ARIA roles, focus management, and keyboard navigation — you bring your own styles. + +### [Installation](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#installation-2) + +```bash +npm install @radix-ui/react-tooltip @radix-ui/react-popover +``` + +### [Tooltip](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#tooltip) + +```tsx +import * as Tooltip from "@radix-ui/react-tooltip"; + +// Provider wraps your app — controls delay behavior globally +export function App() { + return ( + + + + ); +} + +// Individual tooltip +export function DeleteButton() { + return ( + + + + + + + + Delete item + + + + + ); +} + +// CSS +/* +.tooltip-content { + background: #1a1a1a; + color: white; + border-radius: 4px; + padding: 4px 10px; + font-size: 13px; + box-shadow: 0 2px 8px rgba(0,0,0,0.2); + animation-duration: 150ms; + animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); + will-change: transform, opacity; +} +.tooltip-content[data-state='delayed-open'][data-side='top'] { + animation-name: slideDownAndFade; +} +@keyframes slideDownAndFade { + from { opacity: 0; transform: translateY(2px); } + to { opacity: 1; transform: translateY(0); } +} +.tooltip-arrow { + fill: #1a1a1a; +} +*/ +``` + +### [Popover](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#popover) + +```tsx +import * as Popover from "@radix-ui/react-popover"; + +export function FilterPopover() { + return ( + + + + + + + e.preventDefault()} + > +
+

Filter Options

+ + + + + +
+ + + + +
+
+ + + + ✕ + +
+
+
+ ); +} +``` + +### [Tooltip with Tailwind (shadcn/ui Pattern)](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#tooltip-with-tailwind-shadcnui-pattern) + +```tsx +// components/ui/tooltip.tsx — shadcn/ui Tooltip component +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { cn } from "@/lib/utils"; + +const TooltipProvider = TooltipPrimitive.Provider; +const TooltipRoot = TooltipPrimitive.Root; +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +// Export the Tooltip component +export function Tooltip({ + children, + content, + ...props +}: { + children: React.ReactNode; + content: React.ReactNode; +} & React.ComponentPropsWithoutRef) { + return ( + + + {children} + {content} + + + ); +} + +// Usage with Tailwind + + + +``` + +* * * + +## [Feature Comparison](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#feature-comparison) + +| Feature | Floating UI | Tippy.js | Radix Tooltip/Popover | +| --- | --- | --- | --- | +| **Framework** | Any (React, Vue, Svelte) | Any + React | React only | +| **Styling** | Unstyled (bring your own) | Styled (override available) | Unstyled (bring your own) | +| **Accessibility** | Manual (you implement) | Basic | ✅ WAI-ARIA compliant | +| **Focus trap** | `FloatingFocusManager` | No | ✅ Built-in | +| **Keyboard nav** | Via hooks | Basic | ✅ Built-in | +| **Collision detection** | ✅ Advanced | ✅ Via Popper.js | ✅ Via Floating UI | +| **Arrow positioning** | ✅ `FloatingArrow` | ✅ Built-in | ✅ `Tooltip.Arrow` | +| **Animations** | CSS (you define) | ✅ Built-in themes | CSS data-state | +| **Portal** | ✅ `FloatingPortal` | ✅ Auto | ✅ `Portal` | +| **Virtual elements** | ✅ | Limited | No | +| **Bundle size** | ~10kB | ~15kB | ~8kB per primitive | +| **npm weekly** | 12M | 3M | 8M (tooltip) | +| **GitHub stars** | 29k | 11k | 22k (radix-ui/primitives) | +| **TypeScript** | ✅ Full | ✅ | ✅ Full | + +* * * + +## [When to Use Each](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#when-to-use-each) + +**Choose Floating UI if:** + +- Building a component library from scratch (unstyled primitives) +- Need maximum control over positioning behavior and styling +- Framework-agnostic — Vue, Svelte, vanilla JS, or React +- Virtual element positioning (context menus, cursors) +- Complex middleware requirements (custom offset logic) +- Want to understand exactly what's happening — no magic + +**Choose Tippy.js if:** + +- Quick tooltip needed with minimal setup +- Vanilla JS project or legacy codebase +- Want built-in themes and animations without CSS work +- Simple hover tooltips where accessibility is secondary +- Prototyping or internal tools where ARIA isn't critical + +**Choose Radix Tooltip/Popover if:** + +- React/Next.js production application +- Accessibility is required — screen readers, keyboard navigation +- Using shadcn/ui (Radix is the foundation) +- Want compound component API with proper focus management +- Need `asChild` pattern to avoid extra DOM elements +- Building a design system where consumers control all styling + +* * * + +## [Methodology](https://www.pkgpulse.com/blog/floating-ui-vs-tippyjs-vs-radix-tooltip-popover-2026\#methodology) + +Data sourced from Floating UI documentation (floating-ui.com/docs), Tippy.js documentation (atomiks.github.io/tippyjs), Radix UI documentation (radix-ui.com/docs), npm weekly download statistics as of February 2026, GitHub star counts as of February 2026, and community discussions from the React Discord, CSS-Tricks, and web accessibility forums. + +* * * + +_Related: [Radix UI vs Headless UI vs Ariakit](https://www.pkgpulse.com/blog/radix-ui-vs-headless-ui-vs-ariakit-accessible-react-components-2026) for broader headless component comparisons, or [shadcn/ui vs Mantine vs Chakra UI](https://www.pkgpulse.com/blog/shadcn-vs-mantine-vs-chakra-react-component-library-2026) for styled React component libraries._ + +## Comments + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/pkgpulse-linting.md b/.firecrawl/pkgpulse-linting.md new file mode 100644 index 0000000..819f0d3 --- /dev/null +++ b/.firecrawl/pkgpulse-linting.md @@ -0,0 +1,305 @@ +[Skip to main content](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026#main-content) + +## [TL;DR](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#tldr) + +**Biome is production-ready for most JavaScript and TypeScript projects, but ESLint + Prettier is still the right call if you need the full ESLint plugin ecosystem.** Biome's 25x speed advantage is real and meaningful in CI. The formatter is nearly identical to Prettier. The linter covers ~80% of common ESLint rules. What Biome can't replace yet: type-aware lint rules (requires TypeScript language service), framework-specific plugins (eslint-plugin-react-hooks, eslint-plugin-next), and any custom ESLint rules your team has written. Verdict: new projects → Biome. Existing projects with heavy plugin usage → evaluate the gap before switching. + +## [Key Takeaways](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#key-takeaways) + +- **Speed**: Biome formats and lints in ~50ms; ESLint + Prettier takes ~2-3s for same project +- **Coverage**: ~250 lint rules (growing); ESLint has 1000+ with the plugin ecosystem +- **Prettier compatibility**: Biome's formatter matches Prettier output for ~96% of cases +- **Not yet in Biome**: type-aware rules, React Hooks rules, Next.js plugin, custom rule authoring (in roadmap) +- **Configuration**: one config file (`biome.json`) vs two separate configs + +* * * + +## [Speed: The Main Reason to Switch](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#speed-the-main-reason-to-switch) + +```bash +# Real benchmark — a Next.js project with ~150 TypeScript files: + +# ESLint + Prettier (separate runs): +npx eslint . --ext .ts,.tsx → 3.2 seconds +npx prettier --check "**/*.{ts,tsx}" → 1.1 seconds +Total: ~4.3 seconds + +# Biome (lint + format together): +npx biome check . → 0.18 seconds +# 24x faster combined + +# CI impact (running on every PR): +# ESLint + Prettier: 4-5 seconds of your CI job +# Biome: ~0.2 seconds + +# Pre-commit hooks (runs on staged files): +# ESLint + Prettier (lint-staged): ~1.5s per commit +# Biome: ~0.05s per commit +# Developer experience: the difference between "imperceptible" and "I notice this every time" + +# Why Biome is faster: +# → Rust implementation (not Node.js) +# → Parallel processing of files +# → Single pass: lint + format in one traversal +# → No plugin loading overhead (rules are compiled in) +``` + +* * * + +## [Setup: One Config vs Two Configs](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#setup-one-config-vs-two-configs) + +```json +// Biome — biome.json (one file for everything): +{ + "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "correctness": { + "noUnusedVariables": "error" + }, + "style": { + "noParameterAssign": "warn" + }, + "nursery": { + "useSortedClasses": "warn" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "trailingCommas": "all", + "semicolons": "always" + } + }, + "files": { + "ignore": ["node_modules", "dist", ".next"] + } +} + +// Compare to ESLint + Prettier: +// .eslintrc.json OR eslint.config.mjs (ESLint 9 flat config) +// .prettierrc +// .prettierignore +// .eslintignore +// package.json scripts to run both +// lint-staged config for pre-commit hooks + +// Biome replaces all of that with one file. +``` + +* * * + +## [Lint Rules Coverage](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#lint-rules-coverage) + +``` +Biome rules (v1.9, 2026) — grouped by ESLint equivalent: + +✅ Covered well: +→ no-unused-vars, no-undef → biome: correctness/noUnusedVariables +→ no-console → biome: suspicious/noConsole +→ eqeqeq → biome: suspicious/noDoubleEquals +→ no-var → biome: style/noVar +→ prefer-const → biome: style/useConst +→ no-empty → biome: correctness/noEmptyBlockStatements +→ no-duplicate-imports → biome: correctness/noDuplicateObjectKeys +→ arrow-body-style → biome: style/useArrowFunction +→ object-shorthand → biome: style/useShorthandAssign +→ 200+ more rules... + +⚠️ Partially covered / different API: +→ import/order → biome: organizeImports (reorders, doesn't configure) +→ jsx-a11y/* → basic accessibility rules, not all of jsx-a11y + +❌ Not yet in Biome: +→ Type-aware rules (requires TypeScript type checker) + → @typescript-eslint/no-floating-promises + → @typescript-eslint/no-misused-promises + → @typescript-eslint/consistent-return (typed) +→ eslint-plugin-react-hooks (useEffect deps, hooks rules) +→ eslint-plugin-next (app router patterns, image optimization rules) +→ eslint-plugin-import/no-cycle (circular dependency detection) +→ Custom rules your team wrote + +The gap is real but smaller than it was. +Most "critical" lint rules are covered. +The missing ones are important for React/Next.js specifically. +``` + +* * * + +## [Prettier Compatibility: How Close Is It?](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#prettier-compatibility-how-close-is-it) + +```javascript +// Biome's formatter is designed to match Prettier's output +// Real-world compatibility: ~96% identical for JS/TS + +// Cases where they differ (edge cases): +// 1. Long template literals +const query = `SELECT * FROM users WHERE id = ${userId} AND status = 'active' AND created_at > '2024-01-01'`; +// Prettier: keeps on one line if fits, wraps differently +// Biome: similar but not identical on complex expressions + +// 2. Decorators (TypeScript) +// Some class decorator formatting differs slightly + +// 3. Complex JSX expressions +// Multi-line JSX attributes format slightly differently in edge cases + +// For the vast majority of code: identical output. +// The 4% difference is in edge cases you'll rarely hit. + +// Migration from Prettier: +# Run Biome formatter on your entire codebase once: +npx biome format --write . + +# Check what changed (should be minimal): +git diff + +# If there are many meaningful diffs, the code was inconsistently formatted. +# Biome will now be the source of truth. + +# Teams commonly report: +# → 0-20 files changed on a typical codebase +# → Changes are whitespace/trailing comma in edge cases +# → No semantic code changes +``` + +* * * + +## [Integration with Editors and CI](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#integration-with-editors-and-ci) + +```bash +# VS Code — install the Biome extension: +# Extensions: "Biome" (biomejs.biome) +# settings.json: +{ + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "[javascript]": { "editor.defaultFormatter": "biomejs.biome" }, + "[typescript]": { "editor.defaultFormatter": "biomejs.biome" }, + "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" } +} +# The extension works well — fast, accurate + +# Pre-commit hooks (replace lint-staged + eslint/prettier): +# package.json: +{ + "scripts": { + "prepare": "simple-git-hooks" + }, + "simple-git-hooks": { + "pre-commit": "npx lint-staged" + }, + "lint-staged": { + "*.{js,ts,jsx,tsx,json,css}": "biome check --apply --no-errors-on-unmatched" + } +} + +# CI (GitHub Actions): +- name: Lint and Format Check + run: npx biome ci . + # biome ci = check without --write; exits non-zero if issues found + # Replaces separate eslint and prettier --check steps + +# The biome ci command is designed exactly for this use case. +``` + +* * * + +## [Migration from ESLint + Prettier](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#migration-from-eslint--prettier) + +```bash +# Step 1: Initialize Biome +npm install --save-dev --save-exact @biomejs/biome + +# Step 2: Generate config from existing ESLint config +npx biome migrate eslint --include-inspired +# --include-inspired: adds Biome rules "inspired by" your ESLint rules + +# Step 3: Format with Biome once (commit this separately for clean history) +npx biome format --write . +git add -A && git commit -m "chore: migrate formatter to Biome" + +# Step 4: Fix linting issues +npx biome check --apply . +# Some will be auto-fixed. Others need manual attention. + +# Step 5: Find the ESLint rules you'll miss +# Go through your .eslintrc and categorize: +# → Rule covered by Biome? → Remove from ESLint +# → Rule is react-hooks or type-aware? → Keep ESLint for JUST those rules + +# Step 6: The hybrid approach (if you need react-hooks rules): +# Keep ESLint for only what Biome doesn't cover: +# .eslintrc: +{ + "plugins": ["react-hooks"], + "rules": { + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn" + // Nothing else — Biome handles the rest + } +} + +# This is the recommended migration path for React projects. +# Use Biome for formatting + most linting. +# Keep a minimal ESLint config for react-hooks only. +``` + +* * * + +## [Verdict: Should You Switch?](https://www.pkgpulse.com/blog/biome-vs-eslint-prettier-linting-2026\#verdict-should-you-switch) + +``` +New greenfield project (2026): +→ Yes — use Biome from day one +→ Add minimal ESLint config for react-hooks if it's a React project +→ The speed win in CI is real; the single config is a DX improvement +→ The rule gap doesn't matter if you're starting fresh + +Existing project (small, no complex ESLint plugins): +→ Yes — migrate. 2-4 hour job. Net positive. +→ Use the migrate command; review diffs; ship it + +Existing project (React/Next.js, heavy plugin usage): +→ Hybrid approach — Biome for format + most lint, ESLint for react-hooks + next +→ Not "switch" but "add Biome alongside a reduced ESLint config" +→ You still get the speed benefit for most of the work + +Existing project (custom ESLint rules, type-aware rules critical): +→ Not yet — monitor Biome's type-aware rule roadmap +→ Expected in late 2026 based on their public roadmap +→ Reevaluate in 6 months + +The trajectory is clear: Biome is getting better fast. +The rule gap that seemed large in 2024 is substantially smaller in 2026. +Type-aware rules are the final frontier. +``` + +* * * + +_Compare Biome, ESLint, and Prettier download trends at [PkgPulse](https://www.pkgpulse.com/)._ + +See the live comparison + +[View biome vs. eslint on PkgPulse →](https://www.pkgpulse.com/compare/biome-vs-eslint) + +## Comments + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/pkgpulse-packagemgr.md b/.firecrawl/pkgpulse-packagemgr.md new file mode 100644 index 0000000..9c5f777 --- /dev/null +++ b/.firecrawl/pkgpulse-packagemgr.md @@ -0,0 +1,256 @@ +[Skip to main content](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026#main-content) + +## [TL;DR](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#tldr) + +**pnpm is the 2026 default for serious JavaScript projects — content-addressable store, strict dependency isolation, and the best monorepo support.** Bun is 5-10x faster than pnpm on installs but still has edge cases with niche packages. npm is the default that works everywhere but is the slowest. For new projects: pnpm (or Bun if you're already in the Bun ecosystem). For CI speed: Bun's install is often faster than even pnpm's cached install. + +## [Key Takeaways](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#key-takeaways) + +- **Bun install**: 5-10x faster than pnpm, 15-25x faster than npm (measured on real projects) +- **pnpm**: Strictest isolation (prevents phantom dependencies), best workspace support, most compatible +- **npm**: Default, slowest, but universally compatible, `node_modules` phantom deps allowed +- **Disk usage**: pnpm uses ~50% less disk space vs npm (content-addressable store deduplication) +- **Monorepos**: pnpm workspaces > Bun workspaces > npm workspaces (feature parity gap) +- **2026 recommendation**: pnpm for serious projects; Bun install if on Bun runtime already + +* * * + +## [Downloads / Usage](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#downloads--usage) + +| Package Manager | Weekly Downloads | Trend | +| --- | --- | --- | +| `npm` | Default (Node.js) | → Stable | +| `pnpm` | ~7M downloads/week | ↑ Growing | +| `bun` | ~1.5M downloads/week | ↑ Fast growing | + +* * * + +## [Install Speed Benchmarks](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#install-speed-benchmarks) + +``` +Benchmark: Next.js 15 project (1,847 packages) +Environment: M3 MacBook Pro, SSD, cold/warm cache + +COLD INSTALL (no cache, no lockfile): + npm: 82s + pnpm: 31s (2.6x faster than npm) + Bun: 8s (10x faster than npm) + +CACHED INSTALL (lockfile present, store exists): + npm: 45s (reads node_modules hash) + pnpm: 4s (hardlinks from content store) + Bun: 0.8s (binary cache, near-instant) + +CI INSTALL (lockfile present, fresh machine): + npm: 62s + pnpm: 18s (3.4x faster) + Bun: 6s (10x faster) +``` + +* * * + +## [pnpm: The Recommended Default](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#pnpm-the-recommended-default) + +```bash +# Install pnpm: +npm install -g pnpm +# Or via Corepack (Node.js built-in): +corepack enable pnpm + +# Common commands: +pnpm install # Install from lockfile +pnpm add react # Add dependency +pnpm add -D typescript # Add dev dependency +pnpm remove lodash # Remove package +pnpm update --interactive # Interactive update UI +pnpm why lodash # Why is this installed? +pnpm ls # List installed packages +``` + +```yaml +# .npmrc — pnpm configuration: +# Enforce strict peer dependencies: +strict-peer-dependencies=true + +# Hoist patterns (allow certain phantom deps for compat): +public-hoist-pattern[]=*eslint* +public-hoist-pattern[]=*prettier* + +# Save exact versions: +save-exact=true + +# Node linker (for compatibility with some tools): +# node-linker=hoisted # Falls back to npm-style if needed +``` + +```json +// pnpm-workspace.yaml — monorepo config: +{ + "packages": [\ + "apps/*",\ + "packages/*",\ + "tools/*"\ + ] +} +``` + +```bash +# pnpm workspace commands: +pnpm --filter web add react-query # Add to specific package +pnpm --filter "!web" install # Install all except web +pnpm -r run build # Run build in all packages +pnpm --filter web... run build # Build web + its dependencies +pnpm --filter ...web run build # Build packages that depend on web +``` + +### [Why pnpm Over npm](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#why-pnpm-over-npm) + +``` +pnpm advantages: + → No phantom dependencies (package.json must declare everything) + → 50% less disk usage (hardlinks, not copies) + → 3-5x faster installs than npm + → Best workspace support (filtering, recursive) + → Isolated node_modules (each package sees only its deps) + +pnpm limitations: + → Occasional compatibility issues with poorly-written packages + → Slightly steeper learning curve for teams migrating from npm + → Some tools (older ones) expect hoisted node_modules +``` + +* * * + +## [Bun: When Speed Is Everything](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#bun-when-speed-is-everything) + +```bash +# Install Bun: +curl -fsSL https://bun.sh/install | bash + +# Bun install commands (compatible with npm syntax): +bun install # Install from lockfile +bun add react # Add dependency +bun add -d typescript # Add dev dependency (note: -d not -D) +bun remove lodash # Remove +bun update # Update all packages +``` + +```bash +# bun.lock — Bun's lockfile format: +# Binary lockfile (bun.lockb) in older versions +# Text lockfile (bun.lock) in Bun 1.1+ +# Commit bun.lock to version control +``` + +```toml +# bunfig.toml — Bun configuration: +[install] +# Use a private registry: +registry = "https://registry.npmjs.org" +exact = true # Pin exact versions + +[install.scopes] +# Scoped registry: +"@mycompany" = { token = "$NPM_TOKEN", url = "https://npm.mycompany.com" } +``` + +```bash +# Bun workspaces: +# package.json at root: +# { +# "workspaces": ["apps/*", "packages/*"] +# } + +bun install # Installs all workspaces +bun add react --workspace apps/web # Add to specific workspace +bun run --filter '*' build # Run build in all workspaces +``` + +### [Bun Install Limitations](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#bun-install-limitations) + +``` +Known compatibility issues in 2026: + → Some native binaries may not install correctly + → Postinstall scripts: some packages assume npm/node environment + → pnpm-specific workspace.yaml not supported (use package.json workspaces) + → Some packages with complex resolution logic may resolve differently + +Test your project before switching to Bun install in CI: + bun install && bun test # Quick compatibility check +``` + +* * * + +## [npm: Universal Compatibility](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#npm-universal-compatibility) + +```bash +# npm — the universal fallback: +npm install # Install +npm install react # Add +npm install -D typescript # Add dev +npm uninstall lodash # Remove +npm update # Update + +# npm workspaces (basic): +# package.json: { "workspaces": ["apps/*", "packages/*"] } +npm install # Installs all workspaces +npm run build --workspace=apps/web # Run in specific workspace +npm run build --workspaces # Run in all workspaces +``` + +* * * + +## [Corepack: Managing Package Managers](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#corepack-managing-package-managers) + +```json +// package.json — specify exact package manager: +{ + "packageManager": "pnpm@9.15.0" +} +``` + +```bash +# Enable Corepack (Node.js 16+): +corepack enable + +# Now the packageManager field is enforced: +# If you run npm install in a pnpm project, Corepack intercepts: +# "This project requires pnpm@9.15.0. Run 'corepack use pnpm@9.15.0' to switch." + +# In CI — enable Corepack before install: +corepack enable +# Then just run: pnpm install (or whatever packageManager specifies) +``` + +* * * + +## [Decision Guide](https://www.pkgpulse.com/blog/pnpm-vs-bun-vs-npm-package-manager-performance-2026\#decision-guide) + +``` +Use pnpm if: + → New project, want best practices + → Monorepo with multiple packages + → Strict dependency isolation important + → Most compatible choice that's still fast + +Use Bun (install) if: + → Already using Bun as runtime + → CI speed is critical and you've tested compatibility + → Greenfield project with modern packages only + +Use npm if: + → Maximum compatibility needed (legacy projects) + → Required by tooling that expects npm conventions + → Team unfamiliar with pnpm/Bun + → Deploying to environment where only npm is available +``` + +_Compare package manager downloads on [PkgPulse](https://pkgpulse.com/)._ + +## Comments + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/pkgpulse-reactaria-radix.md b/.firecrawl/pkgpulse-reactaria-radix.md new file mode 100644 index 0000000..469e16f --- /dev/null +++ b/.firecrawl/pkgpulse-reactaria-radix.md @@ -0,0 +1,377 @@ +[Skip to main content](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026#main-content) + +Accessibility litigation against web applications increased by 30% year-over-year in 2025. Building accessible UI has gone from "nice to have" to legal requirement for many organizations — and the right headless component library can be the difference between compliance and a lawsuit. React Aria (Adobe) and Radix Primitives are the two dominant choices, and they differ profoundly in philosophy. + +## [TL;DR](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#tldr) + +**Radix Primitives** is the right default for most React applications — pragmatic, well-documented, widely adopted (60K+ stars), and accessible enough for the vast majority of use cases. **React Aria** is the right choice when accessibility is a primary constraint, WCAG compliance is required by contract, or you need the strictest ARIA pattern implementation available. Both are significantly better than building accessible components from scratch. + +## [Key Takeaways](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#key-takeaways) + +- Radix Primitives: ~3.5M weekly downloads across packages, 60K+ GitHub stars +- React Aria Components: ~260K weekly downloads for `react-aria-components` +- Radix: 28 main components; React Aria: 43+ components +- React Aria is built by Adobe (accessibility specialists), used in Adobe Spectrum +- Radix is the foundation of Shadcn UI — the most popular component library of 2025 +- React Aria strictly follows WCAG 2.1 patterns; Radix prioritizes pragmatic DX with good accessibility +- Both ship behavior/logic only — you provide the styles (headless architecture) + +## [The Headless Component Model](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#the-headless-component-model) + +Both libraries are "headless" — they provide behavior, accessibility, and keyboard interactions, but **no visual styling**. You bring your own CSS (Tailwind, CSS Modules, etc.). + +This is the right architecture for component libraries: it separates behavior concerns from visual concerns, making the library usable in any design system. + +## [Radix Primitives](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#radix-primitives) + +**Packages**: `@radix-ui/react-*` (28+ packages) +**Downloads**: ~3.5M weekly (across all packages) +**GitHub stars**: 16K (primitives repo), 60K+ (Radix UI org) +**Created by**: WorkOS + +Radix is the most popular headless component library in the React ecosystem, largely because it powers Shadcn UI. + +### [Installation](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#installation) + +```bash +# Install individual primitives as needed +npm install @radix-ui/react-dialog +npm install @radix-ui/react-dropdown-menu +npm install @radix-ui/react-select +npm install @radix-ui/react-tooltip +``` + +### [Usage Pattern](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#usage-pattern) + +Radix uses a compound component pattern: + +```tsx +import * as Dialog from '@radix-ui/react-dialog'; + +function DeleteConfirmDialog({ onConfirm }: { onConfirm: () => void }) { + return ( + + + + + + + + + + Confirm Deletion + + + This action cannot be undone. All your data will be permanently deleted. + + +
+ + + + + + +
+
+
+
+ ); +} +``` + +### [`asChild` Prop](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#aschild-prop) + +One of Radix's best DX features: `asChild` lets you apply Radix behavior to any component without adding extra DOM elements: + +```tsx +import * as Tooltip from '@radix-ui/react-tooltip'; +import { Link } from 'next/link'; // Or any custom component + +// Without asChild: wraps in a + + + + + {({ close }) => ( + <> + + Confirm Deletion + +

+ This action cannot be undone. +

+
+ + +
+ + )} +
+
+
+ + ); +} +``` + +### [CSS Classes with State](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#css-classes-with-state) + +React Aria uses a slightly different styling approach: + +```tsx +// renderProps pattern for dynamic classes + +``` + +Or with data attributes (similar to Radix): + +```css +.btn[data-pressed] { transform: scale(0.98); } +.btn[data-focused] { outline: 2px solid var(--color-focus); } +.btn[data-disabled] { opacity: 0.5; } +``` + +### [Advanced Accessibility Features](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#advanced-accessibility-features) + +React Aria handles edge cases that simpler libraries miss: + +```tsx +// ComboBox with proper ARIA pattern +import { ComboBox, Item, Label, Input, Popover, ListBox } from 'react-aria-components'; + +function SearchComboBox() { + return ( + + + + + + Afghanistan + Albania + {/* ... */} + + + + ); +} +``` + +React Aria correctly handles: + +- Proper `combobox` ARIA role with `aria-expanded`, `aria-haspopup`, `aria-owns` +- `aria-activedescendant` updates on keyboard navigation +- Correct focus management when popup opens/closes +- Mobile: virtual cursor navigation for screen readers on iOS/Android +- Touch: proper pointer events for touch screen accessibility + +### [Hook-Level API](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#hook-level-api) + +For more control, you can use the individual hooks: + +```typescript +import { useButton } from '@react-aria/button'; +import { useRef } from 'react'; + +function CustomButton({ onPress, children }: Props) { + const ref = useRef(null); + const { buttonProps } = useButton({ onPress }, ref); + + return ( + + ); +} +``` + +## [Accessibility Comparison](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#accessibility-comparison) + +### [ARIA Compliance](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#aria-compliance) + +| Component | Radix | React Aria | +| --- | --- | --- | +| Dialog | WCAG AA | WCAG AAA | +| Select | WCAG AA | WCAG AAA | +| Combobox | Good | Strict | +| Date Picker | Not included | Full ARIA pattern | +| Grid | Not included | Full keyboard nav | +| Virtual cursor | Partial | Full iOS/Android | + +### [Screen Reader Testing](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#screen-reader-testing) + +React Aria tests against: JAWS + Chrome, NVDA + Firefox, VoiceOver + Safari, TalkBack + Chrome, VoiceOver + iOS Safari. + +Radix tests against major screen readers but with less rigor on mobile platforms. + +## [Components Available (React Aria vs Radix)](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#components-available-react-aria-vs-radix) + +| Component | Radix | React Aria | +| --- | --- | --- | +| Button | No (basic) | Yes | +| Checkbox | Yes | Yes | +| Dialog/Modal | Yes | Yes | +| Dropdown Menu | Yes | Yes | +| Select | Yes | Yes | +| Combobox | No | Yes | +| Date Picker | No | Yes | +| Calendar | No | Yes | +| Color Picker | No | Yes | +| Table/Grid | No | Yes | +| Drag & Drop | No | Yes | +| File Trigger | No | Yes | +| Tag Group | No | Yes | + +React Aria has significantly more components, especially for complex interactive patterns. + +## [Bundle Size](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#bundle-size) + +| Package | Gzipped | +| --- | --- | +| `@radix-ui/react-dialog` | ~5.5 kB | +| `@radix-ui/react-dropdown-menu` | ~12 kB | +| `react-aria-components` (tree-shaken) | ~8-20 kB per component | + +## [The Shadcn Effect](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#the-shadcn-effect) + +Radix's dominant download numbers are largely the Shadcn UI effect. Shadcn UI is built on Radix Primitives + Tailwind CSS, and it became the most-copied component library of 2025. Every team using Shadcn is installing Radix under the hood: + +```bash +# This installs @radix-ui/react-dialog automatically +npx shadcn-ui add dialog +``` + +There's no Shadcn-equivalent built on React Aria (though Argos CI migrated from Radix to React Aria and published a migration guide). + +## [When to Choose Each](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#when-to-choose-each) + +**Choose Radix Primitives if:** + +- You're using Shadcn UI (Radix is already included) +- You want the largest community and most resources +- "Accessible enough" is acceptable (not strict WCAG AAA) +- Time-to-production is important +- Your design system is already partially built + +**Choose React Aria if:** + +- WCAG AA/AAA compliance is contractually required +- You need Date Picker, Color Picker, Drag & Drop, or Table with full keyboard navigation +- Your product is used by enterprise customers with accessibility requirements +- You build products used by people with disabilities (government, healthcare, education) +- Mobile screen reader support is critical + +## [A Pragmatic Hybrid](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#a-pragmatic-hybrid) + +Some teams use Radix for most components and React Aria specifically for the complex ones Radix doesn't handle: + +```bash +npm install @radix-ui/react-dialog @radix-ui/react-dropdown-menu # Most UI +npm install react-aria-components # Date pickers, complex patterns +``` + +This is a valid approach — they're both headless libraries that don't conflict. + +## [Compare on PkgPulse](https://www.pkgpulse.com/blog/react-aria-vs-radix-primitives-2026\#compare-on-pkgpulse) + +Track download trends for [Radix UI vs React Aria on PkgPulse](https://pkgpulse.com/). + +See the live comparison + +[View react aria vs. radix on PkgPulse →](https://www.pkgpulse.com/compare/react-aria-vs-radix) + +## Comments + +giscus + +#### 0 reactions + +#### 0 comments + +WritePreview + +[Styling with Markdown is supported](https://guides.github.com/features/mastering-markdown/ "Styling with Markdown is supported") + +[Sign in with GitHub](https://giscus.app/api/oauth/authorize?redirect_uri=https%3A%2F%2Fwww.pkgpulse.com%2Fblog%2Freact-aria-vs-radix-primitives-2026) + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/pkgpulse-shadcn-baseui-radix.md b/.firecrawl/pkgpulse-shadcn-baseui-radix.md new file mode 100644 index 0000000..32df87c --- /dev/null +++ b/.firecrawl/pkgpulse-shadcn-baseui-radix.md @@ -0,0 +1,372 @@ +[Skip to main content](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026#main-content) + +The React component ecosystem in 2026 looks nothing like it did three years ago. shadcn/ui went from "interesting experiment" to the default choice for new React projects, collecting 75,000+ GitHub stars in the process. Radix UI — the primitive layer that shadcn originally built on top of — has slowed down since WorkOS acquired it. And Base UI emerged from the MUI team as a serious contender that addresses Radix's architectural shortcomings with production backing from the world's most-downloaded React component library. + +The three aren't directly competing. They represent different layers of the component stack, and understanding the relationship between them changes which one you should actually install. + +## [TL;DR](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#tldr) + +**For most new React projects: shadcn/ui** — it's the industry default for good reason, the February 2026 Visual Builder reduces setup friction to near zero, and it now supports both Radix and Base UI as the underlying primitive layer. **For custom design systems that need unstyled primitives: Base UI** — it's better-maintained than Radix and has cleaner APIs for complex interactions (comboboxes, multi-select, nested menus). **For existing Radix-based projects: keep Radix** unless you have specific pain points — migration isn't worth the disruption for things that work. + +## [Key Takeaways](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#key-takeaways) + +- **shadcn/ui hit 75,000+ stars** — GitHub's most-starred React UI project for the third consecutive year +- **shadcn/ui now supports Base UI primitives** in addition to Radix — you can choose your primitive layer +- **The February 2026 Visual Builder** lets you configure components visually and copies the exact code — no more manual Tailwind class customization +- **Radix UI was acquired by WorkOS** — updates have slowed, particularly for complex components like Combobox and multi-select +- **Base UI is MUI-backed** with dedicated full-time engineering, not a side project +- **Base UI has better TypeScript types** and cleaner APIs for complex interaction patterns +- **130M monthly npm downloads for Radix** — it's not going anywhere, but active development is slower +- **"Headless UI" is the wrong mental model for shadcn/ui** — it ships with styles, but you own them + +## [At a Glance](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#at-a-glance) + +| | shadcn/ui | Base UI | Radix UI | +| --- | --- | --- | --- | +| GitHub stars | 75,000+ | 4,200+ | 18,700+ | +| npm downloads/month | ~20M | ~5M | ~130M | +| Styling approach | Tailwind (you own the code) | Unstyled primitives | Unstyled primitives | +| Install model | Copy-paste into your repo | npm package | npm package | +| Backing | Community / Vercel interest | MUI team (full-time) | WorkOS (acquired) | +| TypeScript | ✅ | ✅ Excellent | ✅ Good | +| Accessibility | ✅ (via primitives) | ✅ | ✅ | +| Combobox / multi-select | ✅ (via cmdk) | ✅ Better API | ⚠️ Limited | +| Animation primitives | ✅ | ✅ | ✅ | +| CSS Variables theming | ✅ | ✅ | ✅ | +| Visual Builder | ✅ (Feb 2026) | ❌ | ❌ | +| Bundle size (dialog) | ~8KB | ~6KB | ~9KB | +| React version | 18/19 | 18/19 | 18 (19 in progress) | + +## [What Each Actually Is](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#what-each-actually-is) + +Understanding these three tools requires being clear about what layer they occupy: + +**Radix UI** and **Base UI** are _headless component primitives_ — they handle accessibility, keyboard navigation, ARIA attributes, and interaction logic with zero styling. You get behavior, not appearance. You add the CSS. + +**shadcn/ui** is _not a component library in the npm sense_. It's a collection of pre-built components using Tailwind CSS, built on top of headless primitives (originally Radix, now Base UI too). When you run `npx shadcn@latest add button`, it copies the source code for a Button component into your project at `components/ui/button.tsx`. You then own that code — you can modify it however you want. There's no package to update. + +This distinction matters for how you evaluate them: + +```bash +# Radix/Base UI — installed as a dependency, updated via npm +npm install @radix-ui/react-dialog +npm install @base-ui-components/react + +# shadcn/ui — components copied into your project +npx shadcn@latest add dialog +# Creates: components/ui/dialog.tsx +# You own this file. Edit it freely. +``` + +## [shadcn/ui: The Copy-Paste Revolution](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#shadcnui-the-copy-paste-revolution) + +The "install npm package vs copy into your project" distinction sounds like a minor implementation detail. In practice, it changes the entire maintenance model: + +```tsx +// After running: npx shadcn@latest add dialog +// This file is in YOUR repo at components/ui/dialog.tsx + +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root +const DialogTrigger = DialogPrimitive.Trigger +const DialogPortal = DialogPrimitive.Portal + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +``` + +You own this code. You see every className. You can change the animation, the overlay color, the z-index, the transition timing — all without fighting a library's theming system. Want a drawer instead of a centered modal? Edit the classes. + +This is fundamentally different from installing MUI or Mantine, where customizing deeply means fighting overrides. + +## [The February 2026 Visual Builder](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#the-february-2026-visual-builder) + +shadcn's biggest 2026 addition is the Visual Builder — an interactive configurator that lets you adjust variants, sizes, and appearances visually, then copies the exact Tailwind code for your specific configuration: + +```bash +# No install needed — open the builder at: +# https://ui.shadcn.com/builder + +# It generates the exact component code for your choices: +# - Variant (default/destructive/outline/secondary/ghost/link) +# - Size (default/sm/lg/icon) +# - Custom colors/spacing + +# Then copy the output to your components/ui/button.tsx +``` + +For teams that struggled with "what Tailwind classes produce the exact style I want," this removes the main friction point. You configure visually, get the code, own the result. + +## [Switching shadcn/ui from Radix to Base UI](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#switching-shadcnui-from-radix-to-base-ui) + +The February 2026 update that went somewhat under the radar: shadcn/ui now officially supports Base UI as the underlying primitive layer, in addition to Radix: + +```bash +# Initialize shadcn with Base UI primitives (new in 2026) +npx shadcn@latest init --base-ui + +# Or add individual components using Base UI +npx shadcn@latest add dialog --primitive=base-ui +``` + +The resulting components look identical to the user, but the underlying primitive has better TypeScript types and a more consistent API. For new projects, this is worth choosing. For existing Radix-based shadcn projects, migration isn't required. + +## [Base UI: MUI's Headless Bet](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#base-ui-muis-headless-bet) + +Base UI is the MUI (Material UI) team's headless component library, extracted from their earlier "Unstyled components" offering and rebuilt from the ground up. The key difference from Radix: + +**Full-time engineering backing.** Radix is maintained by the WorkOS team with a small core team. Base UI has dedicated MUI engineers working on it as a primary product investment. MUI serves millions of developers — they have strong incentives to keep Base UI production-quality. + +```tsx +// Base UI Dialog — cleaner API surface +import * as Dialog from "@base-ui-components/react/dialog"; + +function ConfirmDialog({ open, onOpenChange, onConfirm }) { + return ( + + + + + Confirm Action + + This action cannot be undone. + +
+ Cancel + +
+
+
+
+ ); +} +``` + +The naming is cleaner: `Dialog.Popup` instead of `DialogContent`, `Dialog.Backdrop` instead of `DialogOverlay`. Minor difference in isolation, but consistent across all components. + +### [Base UI's Combobox: Where It Genuinely Wins](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#base-uis-combobox-where-it-genuinely-wins) + +Radix has been criticized for weak support for complex interaction patterns, particularly comboboxes and multi-select. The Radix Select primitive is intentionally limited, and building a proper searchable multi-select on top of it requires significant custom code: + +```tsx +// Radix approach to searchable select — requires cmdk workaround +import * as Select from "@radix-ui/react-select"; +// Radix Select doesn't support search natively +// shadcn/ui uses cmdk (Command) for this pattern + +// Base UI Combobox — built-in search support +import * as Combobox from "@base-ui-components/react/combobox"; + +function TagSelector({ tags, onSelect }) { + return ( + + + + {tags.map(tag => ( + + {tag.name} + + + ))} + + + ); +} +// Multi-select combobox with search, built natively +``` + +If your application needs complex form interactions — multi-select dropdowns, searchable selects, tag inputs — Base UI's primitive set handles them more naturally than Radix. + +## [Radix UI: The Market Leader with a Question Mark](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#radix-ui-the-market-leader-with-a-question-mark) + +With 130M monthly downloads, Radix UI is the most widely used headless component library in the React ecosystem. It's the primitive layer under shadcn/ui (in its default configuration), Mantine UI, and dozens of other component libraries. + +```tsx +// Radix — the primitives you're probably already using +import * as Dialog from "@radix-ui/react-dialog"; +import * as Select from "@radix-ui/react-select"; +import * as Tooltip from "@radix-ui/react-tooltip"; +import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; + +// Fully accessible, keyboard navigable, WAI-ARIA compliant +// Every property is documented, every pattern tested +``` + +The concern isn't whether Radix is good — it is good. The concern is trajectory. After WorkOS acquired Radix, the update cadence slowed. Several long-standing issues (combobox support, React 19 compatibility updates) moved slowly through the pipeline. The core team is smaller than Base UI's now. + +For projects already built on Radix: don't migrate. The primitives work, the ecosystem is massive, and the API won't break overnight. For new projects choosing between Radix and Base UI as a primitive foundation: Base UI has momentum on its side. + +### [React 19 Compatibility](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#react-19-compatibility) + +```tsx +// Radix — React 19 compatibility update was delayed +// Some @radix-ui/* packages needed updates for React 19's new ref handling +// Most updated by late 2025, but caused upgrade friction + +// Base UI — built with React 19 in mind from the start +// No compatibility issues with the new ref transformation +``` + +Teams upgrading to React 19 encountered some rough edges with Radix packages that took months to fully resolve. Base UI was designed after React 19's changes were known and avoids these patterns. + +## [Bundle Size Reality](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#bundle-size-reality) + +``` +Component: Dialog (open/close with animation) + +Radix @radix-ui/react-dialog: + Package size: 9.2KB (gzipped) + Dependencies: @radix-ui/react-portal, @radix-ui/primitive, etc. + Total with deps: ~28KB + +Base UI @base-ui-components/react (dialog only): + Package size: 6.4KB (gzipped) + Self-contained: yes (fewer cross-package deps) + Total: ~18KB + +shadcn/ui Dialog (your compiled code + Radix): + Component code: ~3KB (Tailwind, compiled) + Runtime: Radix primitives (see above) + Total: ~31KB but includes styles +``` + +The numbers are close enough that bundle size shouldn't drive your decision. What matters more: Radix has many cross-package dependencies (each primitive is a separate package), which can inflate your `node_modules` significantly on larger projects. Base UI is more self-contained. + +## [TypeScript Integration](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#typescript-integration) + +```tsx +// Radix — solid TypeScript, component-level types +import * as Dialog from "@radix-ui/react-dialog"; + +// The types are there but sometimes overly broad +type DialogContentProps = React.ComponentPropsWithoutRef< + typeof Dialog.Content +>; // Works but verbose + +// Base UI — excellent TypeScript, consistent naming +import * as Dialog from "@base-ui-components/react/dialog"; + +// Sub-path imports give you tree-shaking + precise types +type DialogPopupProps = React.ComponentPropsWithoutRef< + typeof Dialog.Popup +>; + +// Base UI components consistently expose: +// - render prop for custom element types +// - className string or function receiving state +// - All HTML attributes via ...props +function StyledDialog({ open }: { open: boolean }) { + return ( + + state.open ? "dialog-open" : "dialog-closed" + } + /> + ); +} +``` + +Base UI's state-based className function is a pattern that makes conditional styling clean without needing `data-[state=open]:` Tailwind selectors. + +## [Choosing Your Stack in 2026](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#choosing-your-stack-in-2026) + +**Recommendation for new Next.js/React projects:** + +```bash +# 1. Install shadcn/ui with Base UI primitives (best of both worlds) +npx shadcn@latest init --base-ui + +# 2. Add components as you need them +npx shadcn@latest add button dialog dropdown-menu tooltip + +# 3. Customize the generated code in components/ui/ +# — You own it. Tailwind classes are yours to change. +``` + +This gives you: shadcn's pre-built components and Visual Builder, Base UI's better primitives under the hood, and ownership of all the code. + +**For custom design systems (no shadcn):** + +```bash +# Base UI — unstyled primitives with modern API +npm install @base-ui-components/react + +# Use CSS modules, CSS-in-JS, or Tailwind — your choice +# Better comboboxes, cleaner types, active development +``` + +**For existing Radix-based projects:** + +```bash +# Don't migrate unless you have specific pain points. +# Radix works. 130M downloads/month says so. +# Wait for shadcn/ui's migration tooling if you ever want to switch. +``` + +## [The Three-Layer Mental Model](https://www.pkgpulse.com/blog/shadcn-ui-vs-base-ui-vs-radix-components-2026\#the-three-layer-mental-model) + +The cleanest mental model for understanding these three: + +``` +Layer 3: shadcn/ui + ↓ Pre-built, Tailwind-styled, copy-pasted into your project + ↓ Uses Layer 2 as primitives (configurable: Radix or Base UI) + +Layer 2: Radix UI / Base UI + ↓ Unstyled, accessible, behavior-only + ↓ You provide all styling + ↓ Both implement ARIA patterns correctly + +Layer 1: Your application + ↓ Uses whichever combination serves your needs +``` + +Most developers don't need to choose between all three. They choose shadcn/ui (which bundles the decision about Layer 2) or they choose a headless primitive directly (Radix or Base UI) for a custom design system. The 2026 update that shadcn/ui supports both Radix and Base UI as primitive layers means you're no longer locked in. + +* * * + +_Compare shadcn/ui, Base UI, and Radix UI download trends at [PkgPulse](https://www.pkgpulse.com/compare/shadcn-ui-vs-radix-ui)._ + +_Related: [React 19 vs React 18 2026](https://www.pkgpulse.com/blog/react-19-vs-react-18-2026) · [Tailwind CSS vs CSS Modules 2026](https://www.pkgpulse.com/blog/tailwind-vs-css-modules-2026) · [MUI vs Ant Design 2026](https://www.pkgpulse.com/blog/mui-vs-ant-design-2026)_ + +See the live comparison + +[View shadcn ui vs. radix ui on PkgPulse →](https://www.pkgpulse.com/compare/shadcn-ui-vs-radix-ui) + +## Comments + +giscus + +#### 0 reactions + +#### 0 comments + +WritePreview + +[Styling with Markdown is supported](https://guides.github.com/features/mastering-markdown/ "Styling with Markdown is supported") + +[Sign in with GitHub](https://giscus.app/api/oauth/authorize?redirect_uri=https%3A%2F%2Fwww.pkgpulse.com%2Fblog%2Fshadcn-ui-vs-base-ui-vs-radix-components-2026) + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/pkgpulse-testing.md b/.firecrawl/pkgpulse-testing.md new file mode 100644 index 0000000..42d7bbf --- /dev/null +++ b/.firecrawl/pkgpulse-testing.md @@ -0,0 +1,302 @@ +[Skip to main content](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026#main-content) + +## [TL;DR](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#tldr) + +**The modern testing stack: Vitest (unit/integration) + Playwright (E2E).** Jest is still running millions of tests across the npm ecosystem, but new projects default to Vitest because it shares Vite's config, runs tests in parallel by default, and is 5-10x faster. Playwright replaced Cypress as the E2E tool of choice — better multi-tab support, less flakiness, and first-class TypeScript. The old stack (Jest + Enzyme/React Testing Library + Cypress) still works, but the new stack (Vitest + Testing Library + Playwright) is faster, simpler, and better. + +## [Key Takeaways](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#key-takeaways) + +- **Vitest**: Jest-compatible API, Vite-native, ~10x faster, TypeScript without setup +- **Jest**: 40M+ weekly downloads (legacy), still excellent, no reason to migrate working tests +- **Playwright**: multi-browser E2E, trace viewer, 80%+ market share in new projects +- **Cypress**: real-time browser view is great DX but slower and less capable than Playwright +- **Testing Library**: the default React component testing approach — framework-agnostic + +* * * + +## [Unit Testing: Vitest vs Jest](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#unit-testing-vitest-vs-jest) + +```typescript +// The APIs are nearly identical — migration is usually find-and-replace: + +// ─── Jest ─── +// jest.config.js +module.exports = { + transform: { '^.+\\.tsx?$': ['ts-jest', {}] }, // setup required + testEnvironment: 'jsdom', +}; + +// test file: +import { sum } from './math'; +describe('math utils', () => { + test('adds two numbers', () => { + expect(sum(1, 2)).toBe(3); + }); + it('handles negatives', () => { + expect(sum(-1, -2)).toBe(-3); + }); +}); + +// ─── Vitest ─── +// vite.config.ts (reuses existing Vite config!) +import { defineConfig } from 'vite'; +export default defineConfig({ + test: { + environment: 'jsdom', + globals: true, // optional: makes describe/test/expect global without import + }, +}); + +// test file — identical to Jest: +import { sum } from './math'; +describe('math utils', () => { + test('adds two numbers', () => { + expect(sum(1, 2)).toBe(3); + }); +}); + +// Performance comparison (500 unit tests, React project): +// Jest (with ts-jest): 8.4s +// Jest (with babel-jest): 11.2s +// Vitest: 1.8s 🏆 + +// Why Vitest is faster: +// → Uses esbuild for transforms (same as Vite dev server) +// → Parallel by default (worker threads, one per test file) +// → No separate config for TS — shares Vite's esbuild config +// → Module resolution uses Vite's resolver (no duplicate setup) +``` + +* * * + +## [Component Testing with React Testing Library](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#component-testing-with-react-testing-library) + +```typescript +// Testing Library works with both Jest and Vitest — same API: + +// Setup (Vitest): +// package.json: +{ + "test": "vitest", + "dependencies": { + "@testing-library/react": "^15", + "@testing-library/user-event": "^14", + "@testing-library/jest-dom": "^6" + } +} + +// vitest.setup.ts: +import '@testing-library/jest-dom/vitest'; // extends expect with toBeInDocument etc. + +// vite.config.ts: +export default defineConfig({ + test: { + environment: 'jsdom', + setupFiles: ['./vitest.setup.ts'], + }, +}); + +// Component test: +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { LoginForm } from './LoginForm'; + +describe('LoginForm', () => { + it('submits with email and password', async () => { + const mockSubmit = vi.fn(); // vi.fn() instead of jest.fn() + render(); + + await userEvent.type(screen.getByLabelText('Email'), 'user@example.com'); + await userEvent.type(screen.getByLabelText('Password'), 'password123'); + await userEvent.click(screen.getByRole('button', { name: /sign in/i })); + + await waitFor(() => { + expect(mockSubmit).toHaveBeenCalledWith({ + email: 'user@example.com', + password: 'password123', + }); + }); + }); + + it('shows error for invalid email', async () => { + render(); + await userEvent.type(screen.getByLabelText('Email'), 'not-an-email'); + await userEvent.click(screen.getByRole('button', { name: /sign in/i })); + expect(screen.getByText(/invalid email/i)).toBeInTheDocument(); + }); +}); +``` + +* * * + +## [E2E Testing: Playwright vs Cypress](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#e2e-testing-playwright-vs-cypress) + +```typescript +// ─── Playwright ─── +// playwright.config.ts +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testDir: './e2e', + use: { + baseURL: 'http://localhost:3000', + trace: 'on-first-retry', // Capture traces on failure + }, + projects: [\ + { name: 'chromium', use: { browserName: 'chromium' } },\ + { name: 'firefox', use: { browserName: 'firefox' } },\ + { name: 'safari', use: { browserName: 'webkit' } },\ + { name: 'mobile', use: { ...devices['iPhone 13'] } },\ + ], +}); + +// e2e/auth.spec.ts +import { test, expect } from '@playwright/test'; + +test('user can log in', async ({ page }) => { + await page.goto('/login'); + await page.fill('[data-testid="email"]', 'user@example.com'); + await page.fill('[data-testid="password"]', 'password123'); + await page.click('button[type="submit"]'); + await expect(page).toHaveURL('/dashboard'); + await expect(page.locator('h1')).toHaveText('Welcome back'); +}); + +// Multi-tab test (Playwright exclusive): +test('cart persists across tabs', async ({ context }) => { + const page1 = await context.newPage(); + const page2 = await context.newPage(); + await page1.goto('/product/1'); + await page1.click('button:text("Add to Cart")'); + await page2.goto('/cart'); + await expect(page2.locator('.cart-item')).toHaveCount(1); +}); + +// API mocking in tests: +test('shows error when API fails', async ({ page }) => { + await page.route('**/api/users', route => route.fulfill({ + status: 500, + body: JSON.stringify({ error: 'Server error' }), + })); + await page.goto('/users'); + await expect(page.locator('.error-message')).toBeVisible(); +}); +``` + +* * * + +## [Playwright Trace Viewer: Debugging E2E Failures](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#playwright-trace-viewer-debugging-e2e-failures) + +```bash +# Run tests with trace on failure: +npx playwright test --trace on + +# Or configure in playwright.config.ts: +use: { trace: 'on-first-retry' } + +# After a failure, view the trace: +npx playwright show-trace test-results/trace.zip + +# The trace viewer shows: +# → Screenshot at each action +# → Network requests and responses +# → Console logs and errors +# → DOM snapshots you can inspect +# → Timeline of the test execution +# This replaces hours of debugging with 5 minutes of trace review + +# Run specific test in headed mode (see the browser): +npx playwright test --headed auth.spec.ts + +# Generate test code by recording browser actions: +npx playwright codegen http://localhost:3000 +# → Opens browser, records your clicks, generates test code +``` + +* * * + +## [Complete Testing Stack Setup](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#complete-testing-stack-setup) + +```bash +# Install everything for the modern stack: +npm install --save-dev \ + vitest \ + @vitest/ui \ # visual test runner UI + jsdom \ # browser environment for unit tests + @testing-library/react \ + @testing-library/user-event \ + @testing-library/jest-dom \ + @playwright/test + +# Install Playwright browsers (one-time): +npx playwright install + +# package.json scripts: +{ + "scripts": { + "test": "vitest", + "test:ui": "vitest --ui", + "test:watch": "vitest --watch", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", + "test:all": "vitest run && playwright test" + } +} + +# File structure: +src/ + components/ + Button/ + Button.tsx + Button.test.tsx ← unit/integration test (Vitest + Testing Library) + utils/ + math.test.ts ← unit test +e2e/ + auth.spec.ts ← E2E test (Playwright) + checkout.spec.ts +playwright.config.ts +vite.config.ts ← Vitest config lives here +``` + +* * * + +## [When to Keep Jest](https://www.pkgpulse.com/blog/vitest-jest-playwright-complete-testing-stack-2026\#when-to-keep-jest) + +``` +Keep Jest when: +→ Existing test suite works — don't migrate for the sake of it +→ Your project doesn't use Vite (Create React App, custom Webpack setup) +→ You use Jest-specific features (jest.spyOn, jest.useFakeTimers) extensively +→ Your team knows Jest deeply and migration would cause disruption + +Migrate to Vitest when: +→ New project (always use Vitest) +→ Test suite is slow and painful (10+ second runs for unit tests) +→ You've already migrated to Vite for bundling +→ TypeScript setup with ts-jest is causing friction + +Migration process (from Jest to Vitest): +1. npx vitest-migration # automated codemods available +2. Replace jest.fn() → vi.fn() +3. Replace jest.mock() → vi.mock() +4. Update jest.config.js → vitest config in vite.config.ts +5. Run tests: expect ~95% to pass without changes + +The compatibility is excellent — most Jest tests run on Vitest unchanged. +``` + +* * * + +_Compare Vitest, Jest, Playwright, and other testing library trends at [PkgPulse](https://www.pkgpulse.com/compare/vitest-vs-jest)._ + +See the live comparison + +[View vitest vs. jest on PkgPulse →](https://www.pkgpulse.com/compare/vitest-vs-jest) + +## Comments + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/pkgpulse-validation.md b/.firecrawl/pkgpulse-validation.md new file mode 100644 index 0000000..fef06e6 --- /dev/null +++ b/.firecrawl/pkgpulse-validation.md @@ -0,0 +1,290 @@ +[Skip to main content](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026#main-content) + +## [TL;DR](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#tldr) + +**Zod v4 remains the default for TypeScript validation — but Valibot (8KB vs Zod's 60KB) and ArkType (fastest runtime parsing) are compelling for performance-critical use cases.** TypeBox generates JSON Schema natively, making it the best choice for OpenAPI/Swagger integration. For new projects: Zod v4. For edge/bundle-size-critical: Valibot. For OpenAPI: TypeBox. For maximum runtime speed: ArkType. + +## [Key Takeaways](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#key-takeaways) + +- **Zod v4**: 60KB, 10M+ downloads/week, best ecosystem (react-hook-form, trpc, drizzle) +- **Valibot**: 8KB, tree-shakable, modular API, ~10x smaller than Zod +- **ArkType**: Fastest parser (3-10x faster than Zod), TypeScript syntax strings +- **TypeBox**: JSON Schema native, `Static` TypeScript types +- **Performance**: ArkType > Valibot > TypeBox > Zod (but all are "fast enough" for most apps) +- **Ecosystem**: Zod integrates with everything; others are catching up + +* * * + +## [Downloads](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#downloads) + +| Package | Weekly Downloads | Trend | +| --- | --- | --- | +| `zod` | ~10M | ↑ Growing | +| `@sinclair/typebox` | ~6M | ↑ Growing | +| `valibot` | ~1M | ↑ Fast growing | +| `arktype` | ~200K | ↑ Growing | + +* * * + +## [Performance Benchmarks](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#performance-benchmarks) + +``` +Schema: User object with 10 fields, nested address, array of tags + +Parsing 100,000 objects: + ArkType: 45ms ← Fastest + Valibot: 120ms + TypeBox: 180ms + Zod v4: 280ms (v4 is 2x faster than v3's ~580ms) + +Bundle size (minified + gzipped): + Valibot: 8KB ← Smallest + ArkType: 12KB + TypeBox: 60KB (includes JSON Schema types) + Zod v4: 60KB + +Type inference speed (tsc, 50-field schema): + ArkType: ~200ms + Zod v4: ~450ms + Valibot: ~600ms + TypeBox: ~300ms +``` + +* * * + +## [Zod v4: The Default](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#zod-v4-the-default) + +```typescript +// Zod v4 — new features and performance improvements: +import { z } from 'zod'; + +// Basic schema (same as v3): +const UserSchema = z.object({ + id: z.string().cuid2(), + email: z.string().email(), + name: z.string().min(2).max(100), + age: z.number().int().min(0).max(150).optional(), + role: z.enum(['user', 'admin', 'moderator']), + tags: z.array(z.string()).max(10), + address: z.object({ + street: z.string(), + city: z.string(), + country: z.string().length(2), // ISO 2-letter + }).optional(), + metadata: z.record(z.string(), z.unknown()), + createdAt: z.coerce.date(), // Auto-coerce string → Date +}); + +type User = z.infer; + +// Zod v4 new: z.file() for Blob/File +const UploadSchema = z.object({ + file: z.instanceof(File) + .refine(f => f.size < 5_000_000, 'Max 5MB') + .refine(f => ['image/jpeg', 'image/png', 'image/webp'].includes(f.type), 'Must be JPEG/PNG/WebP'), + caption: z.string().max(500).optional(), +}); + +// Zod v4 new: z.pipe() for chained transforms +const ParsedDateSchema = z + .string() + .pipe(z.coerce.date()); // string → validated Date + +// Zod v4 new: z.toJSONSchema() +const jsonSchema = z.toJSONSchema(UserSchema); +// Generates standard JSON Schema — useful for OpenAPI docs + +// Error formatting (v4 — cleaner): +const result = UserSchema.safeParse({ email: 'bad' }); +if (!result.success) { + const errors = result.error.flatten(); + // { fieldErrors: { email: ['Invalid email'] }, formErrors: [] } +} +``` + +* * * + +## [Valibot: Bundle-Size Champion](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#valibot-bundle-size-champion) + +```typescript +// Valibot — modular, tree-shakable: +import { + object, string, number, array, optional, enum_, + email, minLength, maxLength, integer, minValue, maxValue, + parse, safeParse, flatten, + type InferInput, type InferOutput, +} from 'valibot'; + +// Only imports what you use — tree-shaking reduces bundle to ~2-5KB for simple schemas +const UserSchema = object({ + id: string([minLength(1)]), + email: string([email()]), + name: string([minLength(2), maxLength(100)]), + age: optional(number([integer(), minValue(0), maxValue(150)])), + role: enum_(['user', 'admin', 'moderator']), + tags: array(string(), [maxLength(10)]), +}); + +type User = InferInput; + +// Parse (throws on error): +const user = parse(UserSchema, rawData); + +// Safe parse (returns result/error): +const result = safeParse(UserSchema, rawData); +if (result.success) { + console.log(result.output); +} else { + const errors = flatten(result.issues); + // { nested: { email: ['Invalid email'] } } +} +``` + +```typescript +// Valibot with React Hook Form: +import { valibotResolver } from '@hookform/resolvers/valibot'; +import { useForm } from 'react-hook-form'; + +function SignupForm() { + const { register, handleSubmit, formState: { errors } } = useForm({ + resolver: valibotResolver(UserSchema), + }); + + return ( + + + {errors.email && {errors.email.message}} + + ); +} +``` + +* * * + +## [ArkType: Fastest Runtime + TypeScript Syntax](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#arktype-fastest-runtime--typescript-syntax) + +```typescript +// ArkType — TypeScript-syntax strings for schemas: +import { type } from 'arktype'; + +// Syntax feels like writing TypeScript: +const User = type({ + id: 'string', + email: 'string.email', + name: '2 <= string <= 100', // min/max length shorthand! + age: 'number.integer | undefined', + role: '"user" | "admin" | "moderator"', + tags: 'string[] <= 10', // array with max length + createdAt: 'Date', +}); + +type User = typeof User.infer; + +// Parse: +const result = User(rawData); + +// ArkType returns morph (with parse) or error: +if (result instanceof type.errors) { + console.log(result.summary); // Human-readable error +} else { + // result is User +} + +// ArkType advanced: morphs (transform) +const ParsedDate = type('string').pipe(s => new Date(s), 'Date'); + +// Recursive types (Zod struggles here): +const TreeNode = type({ + value: 'number', + children: 'TreeNode[]', // Self-referencing! +}).describe('TreeNode'); // Named for error messages +``` + +* * * + +## [TypeBox: JSON Schema Native](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#typebox-json-schema-native) + +```typescript +// TypeBox — generates JSON Schema, used in Fastify/Hono: +import { Type, Static } from '@sinclair/typebox'; +import { Value } from '@sinclair/typebox/value'; + +// TypeBox schema IS JSON Schema: +const UserSchema = Type.Object({ + id: Type.String({ format: 'uuid' }), + email: Type.String({ format: 'email' }), + name: Type.String({ minLength: 2, maxLength: 100 }), + age: Type.Optional(Type.Integer({ minimum: 0, maximum: 150 })), + role: Type.Union([\ + Type.Literal('user'),\ + Type.Literal('admin'),\ + Type.Literal('moderator'),\ + ]), + tags: Type.Array(Type.String(), { maxItems: 10 }), +}); + +// TypeScript type from schema: +type User = Static; + +// Validate: +const result = Value.Check(UserSchema, rawData); +if (!result) { + const errors = [...Value.Errors(UserSchema, rawData)]; + // [{ path: '/email', message: 'Expected string' }] +} + +// TypeBox + Hono (validated routes with OpenAPI): +import { Hono } from 'hono'; +import { describeRoute } from 'hono-openapi'; + +const app = new Hono(); +app.post('/users', + describeRoute({ + requestBody: { content: { 'application/json': { schema: UserSchema } } }, + responses: { 201: { description: 'Created' } }, + }), + async (c) => { /* handler */ } +); + +// Export OpenAPI spec: +// app.doc('/openapi.json', { openapi: '3.0.0', info: { title: 'API', version: '1' } }) +``` + +* * * + +## [Decision Guide](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#decision-guide) + +``` +Use Zod v4 if: + → Default choice — best ecosystem (react-hook-form, trpc, drizzle, next-safe-action) + → Team already knows Zod v3 (v4 is mostly backwards compatible) + → Need broad library compatibility + → Bundle size is not a constraint + +Use Valibot if: + → Edge runtime / bundle size critical (<5KB budget) + → Want tree-shakable, pay-only-for-what-you-use + → Cloudflare Workers or similar constrained environments + +Use ArkType if: + → Parsing millions of objects (backend hot path) + → Love TypeScript-native syntax strings + → Need recursive types easily + → Fastest possible validation + +Use TypeBox if: + → Building OpenAPI/Swagger documentation + → Using Fastify (TypeBox is Fastify's native schema) + → Need JSON Schema output for other tools + → API validation that also generates docs +``` + +_Compare Zod, Valibot, ArkType, and TypeBox on [PkgPulse](https://pkgpulse.com/compare/zod-vs-valibot)._ + +## Comments + +### The 2026 JavaScript Stack Cheatsheet + +One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. + +Get the Free Cheatsheet \ No newline at end of file diff --git a/.firecrawl/shadcn-claude-settings.md b/.firecrawl/shadcn-claude-settings.md new file mode 100644 index 0000000..87eb061 --- /dev/null +++ b/.firecrawl/shadcn-claude-settings.md @@ -0,0 +1,252 @@ +## TL;DR + +Claude Code guesses your component APIs, theme tokens, and project setup — and gets them wrong half the time. shadcn/ui ships 3 tools (Skills, MCP Server, Preset) that inject real project context into your AI. Setup takes 5 minutes, 3 commands. + +``` +# 1. Project context +npx skills add shadcn/ui + +# 2. Live docs +claude mcp add shadcn -- npx shadcn@latest mcp + +# 3. Design system +npx shadcn@latest init --preset a1Dg5eFl +``` + +Enter fullscreen modeExit fullscreen mode + +* * * + +## The Problem: AI-Generated UI Looks Inconsistent + +If you've used Claude Code to build UI, you've probably seen this: + +- The AI generates a ` + +// After: AI adds loading state by reading the component + +``` + +## [Why AI gets shadcn/ui so well](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#why-ai-gets-shadcnui-so-well) + +Think about it—AI is really good at pattern recognition and code modification. But it's only as good as what it can see. + +With Tailwind CSS, everything's explicit in your React components. `text-blue-500` always means the same thing. Your AI coding tool reads `hover:bg-blue-600 focus:ring-2 focus:ring-blue-500` and immediately knows what's going on. No theme system to decode, no CSS-in-JS abstractions to parse in your TypeScript files. + +After your AI sees a couple shadcn/ui React components, it starts picking up on the patterns. How variants work with TypeScript, how you structure Next.js layouts, even your accessibility patterns. It's like having a junior dev who learns your coding style really, really fast. + +The difference? With traditional libraries, all these patterns are hidden in theme configs and style overrides. With shadcn/ui, they're right there in the component files where your AI can see them. + +``` +// What your AI sees in button.tsx +const buttonVariants = cva( + "inline-flex items-center justify-center rounded-md text-sm font-medium", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: "border border-input bg-background hover:bg-accent", + // AI can easily add new variants here + loading: "bg-primary/50 text-primary-foreground cursor-not-allowed", + }, + }, + } +); +``` + +## [What this actually means for your workflow](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#what-this-actually-means-for-your-workflow) + +Here's the crazy part: shadcn/ui wasn't designed for AI at all. It was designed to solve the developer control problem. But in doing that, it accidentally solved the AI problem too. + +Your AI coding tool doesn't need docs anymore—it just reads your React components with TypeScript. When it suggests changes, they actually work because it understands the real implementation in your Next.js application, not some abstract API description. And the more you customize your components, the better your AI gets at understanding your specific style. + +## [The bigger pattern emerging](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#the-bigger-pattern-emerging) + +You know what's interesting? This transparency thing isn't just winning in React and Next.js development. Look around—AI coding tools work better with anything they can actually read and understand. + +Terraform configs vs complex deployment abstractions. Raw SQL schemas vs ORM magic. OpenAPI specs vs undocumented REST APIs. TypeScript interfaces vs any types. Every time, the transparent, declarative approach wins when AI gets involved. + +shadcn/ui just happened to stumble onto this principle first in the React component library space. But the pattern is clear: if you want to build something that works well with AI coding tools, make it readable with TypeScript, make it explicit, and put it where AI can see it in your Next.js codebase. + +## [What comes next](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#what-comes-next) + +We're still in the early days, but the trajectory is clear. As AI coding tools get better at understanding React component patterns with TypeScript, they amplify shadcn/ui's transparency advantage. More Next.js projects adopt these patterns, creating better training data, which makes AI tools even more effective. It's a virtuous cycle. + +We're not just building better UIs anymore. We're building UIs that think. + +## [The simple truth](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#the-simple-truth) + +Here's the beautiful part: you don't need to change anything. Use shadcn/ui React components with TypeScript exactly as you normally would in your Next.js application. The AI compatibility comes from the transparency, not special APIs or patterns. + +shadcn/ui accidentally solved the AI coding tool compatibility problem by solving the developer control problem. When you own your React component code with TypeScript, both you and your AI can understand it. The result? A development experience that feels like having a pair programmer who actually understands your Next.js codebase. + +## [AI-Friendly Components to Try](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#ai-friendly-components-to-try) + +These React components work exceptionally well with AI coding tools because of their transparent, readable structure: + +[**Button** \\ +Variants and states that AI can easily read and modify](https://www.shadcn.io/ui/button) [**Input** \\ +Form components with clear TypeScript interfaces](https://www.shadcn.io/ui/input) [**Dialog** \\ +Complex components AI can understand and customize](https://www.shadcn.io/ui/dialog) [**Form** \\ +Validation patterns AI can replicate and extend](https://www.shadcn.io/ui/form) [**Data Table** \\ +Advanced components with clear sorting and filtering logic](https://www.shadcn.io/ui/data-table) [**Command** \\ +Search interfaces AI can enhance with new features](https://www.shadcn.io/ui/command) + +## [Ready to try it yourself?](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#ready-to-try-it-yourself) + +Pick your framework and get started with shadcn/ui. The [installation guide](https://www.shadcn.io/ui/installation-guide) walks you through setup for Next.js, Vite, Remix, and more. Takes about 5 minutes to get your first component working. + +Once you're set up, here's where to go next: + +1. Browse [official components](https://www.shadcn.io/ui) for forms, tables, and UI elements +2. Add [charts](https://www.shadcn.io/charts) for data visualization +3. Explore [community components](https://www.shadcn.io/components) for extended functionality +4. Add useful [hooks](https://www.shadcn.io/hooks) to enhance your components +5. Use pre-built [blocks](https://www.shadcn.io/blocks) to quickly build common layouts + +Then ask your AI assistant to modify something. Watch it actually understand your components instead of guessing from docs. You'll never want to go back to black box libraries again. + +## [Questions you're probably thinking](https://www.shadcn.io/ui/why-ai-coding-tools-love-shadcn-ui\#questions-youre-probably-thinking) + +### Do I need to learn new patterns for AI-assisted development? + +### Will this work with future AI coding tools? + +### Are other libraries adopting this approach? + +Was this page helpful? + +[Sign in](https://www.shadcn.io/sign-in) to leave feedback. + +[Shadcn UI React Components\\ +\\ +Copy-paste React components built with Radix UI and Tailwind CSS. Open source component library for Next.js with TypeScript support and full code ownership.](https://www.shadcn.io/ui) [Shadcn Installation Guide\\ +\\ +Setup guides for shadcn/ui with Next.js, Vite, Remix, Laravel, Astro, and more React frameworks. Get started with TypeScript components quickly.](https://www.shadcn.io/ui/installation-guide) \ No newline at end of file diff --git a/.firecrawl/solid-primitives-context.md b/.firecrawl/solid-primitives-context.md new file mode 100644 index 0000000..d4e153f --- /dev/null +++ b/.firecrawl/solid-primitives-context.md @@ -0,0 +1,151 @@ +Context + +# Context + +Context + +Context + +Context + +Size + +263 B +[NPM\\ +\\ +v0.3.1](https://www.npmjs.com/package//@solid-primitives/context) +Stage + +2 + +## [\#](https://primitives.solidjs.community/package/context/\#installation) Installation + +Copy + +npm install @solid-primitives/context + +Copy + +yarn add @solid-primitives/context + +Copy + +pnpm add @solid-primitives/context + +## [\#](https://primitives.solidjs.community/package/context/\#readme) Readme + +Primitives simplifying the creation and use of SolidJS Context API. + +- [`createContextProvider`](https://primitives.solidjs.community/package/context/#createcontextprovider) \- Create the Context Provider component and useContext function with types inferred from the factory function. +- [`MultiProvider`](https://primitives.solidjs.community/package/context/#multiprovider) \- A component that allows you to provide multiple contexts at once. + +## [\#](https://primitives.solidjs.community/package/context/\#createcontextprovider)`createContextProvider` + +Create the Context Provider component and useContext function with types inferred from the factory function. + +### [\#](https://primitives.solidjs.community/package/context/\#how-to-use-it) How to use it + +Given a factory function, `createContextProvider` creates a SolidJS Context and returns both a Provider component for setting the context, and a useContext helper for getting the context. The factory function gets called when the provider component gets executed; all `props` of the provider component get passed into the factory function, and what it returns will be available in the contexts for all the underlying components. The types of the provider props and context are inferred from the factory function. + +```tsx +import { createContextProvider } from "@solid-primitives/context"; + +const [CounterProvider, useCounter] = createContextProvider((props: { initial: number }) => { + const [count, setCount] = createSignal(props.initial); + const increment = () => setCount(count() + 1); + return { count, increment }; +}); + +// Provide the context + + +; + +// Use the context in a child component +const ctx = useCounter(); +ctx; // T: { count: () => number; increment: () => void; } | undefined +``` + +### [\#](https://primitives.solidjs.community/package/context/\#providing-context-fallback) Providing context fallback + +The `createContextProvider` primitive takes a second, optional argument for providing context defaults for when the context wouldn't be provided higher in the component tree. +Providing a fallback also removes `undefined` from `T | undefined` return type of the `useContext` function. + +```ts +const [CounterProvider, useCounter] = createContextProvider( + () => { + const [count, setCount] = createSignal(0); + const increment = () => setCount(count() + 1); + return { count, increment }; + }, + { + count: () => 0, + increment: () => {}, + }, +); + +// then when using the context: +const { count } = useCounter(); +``` + +Definite context types without defaults: + +```ts +const useDefiniteCounter = () => useCounter()!; +``` + +### [\#](https://primitives.solidjs.community/package/context/\#demo) Demo + +[https://codesandbox.io/s/solid-primitives-context-demo-oqyie2?file=/index.tsx](https://codesandbox.io/s/solid-primitives-context-demo-oqyie2?file=/index.tsx) + +## [\#](https://primitives.solidjs.community/package/context/\#multiprovider)`MultiProvider` + +A component that allows you to provide multiple contexts at once. + +It will work exactly like nesting multiple providers as separate components, but it will save you from the nesting. + +### [\#](https://primitives.solidjs.community/package/context/\#how-to-use-it-1) How to use it + +`MultiProvider` takes only a single `values` with a key-value pair of the context and the value to provide. + +> **Note** +> Values list is evaluated in order, so the context values will be provided in the same way as if you were nesting the providers. + +```tsx +import { MultiProvider } from "@solid-primitives/context"; + +// before + + + + + + + + + + +; + +// after + + +; +``` + +> **Warning** +> Components and values passed to `MultiProvider` will be evaluated only once, so make sure that the structure is static. If is isn't, please use nested provider components instead. + +## [\#](https://primitives.solidjs.community/package/context/\#changelog) Changelog + +See [CHANGELOG.md](https://github.com/solidjs-community/solid-primitives/blob/main/packages/context/CHANGELOG.md) \ No newline at end of file diff --git a/.firecrawl/solidjs-ui-libs.md b/.firecrawl/solidjs-ui-libs.md new file mode 100644 index 0000000..211c23b --- /dev/null +++ b/.firecrawl/solidjs-ui-libs.md @@ -0,0 +1,281 @@ +As more developers are adopting [SolidJS](https://www.solidjs.com/?ref=yon.fun) for its fine-grained reactivity and impressive performance, finding the right UI library that is easy to use, flexible, and efficient has become even more important. + +The right UI components can make a significant difference in how quickly and smoothly your project gets completed, ensuring both a great developer experience and user satisfaction. + +According to [recent statistics](https://www.statista.com/statistics/1124699/worldwide-developer-survey-most-used-frameworks-web/?ref=yon.fun), SolidJS is used by 1.2% of developers worldwide (as of 2024), indicating that more developers are seeking powerful tools to improve their productivity. + +![Solid.js Usage 1.2% as of 2024](https://yon.fun/content/images/2024/11/image-9.png)SolidJS Usage Worldwide as of 2024 + +In this article, we'll talk about some of the [best UI libraries for SolidJS](https://yon.fun/solidjs-ui-libs/), ranked by how complex they are, their features, and how easy they are to use. + +Let's explore what each library offers and how it can fit your development needs! + +[Top 15 Best Lightweight CSS Frameworks (JS-Free)\\ +\\ +Find the best lightweight CSS frameworks for responsive, customizable, and JS-free solutions. Discover our top 15 picks, designed to simplify your design process and improve website performance.\\ +\\ +![](https://yon.fun/content/images/size/w256h256/2024/03/favicon-60.png)The Art of Dev.Ion Prodan\\ +\\ +![](https://yon.fun/content/images/2023/04/Top-15-Best-Lightweight-CSS-Frameworks.png)](https://yon.fun/top-10-css-frameworks/) + +## 1\. Kobalte + +![](https://yon.fun/content/images/2024/11/image-17.png)Kobalte Homepage + +[Kobalte](https://kobalte.dev/docs/core/overview/introduction?ref=yon.fun) is a UI toolkit designed to build accessible web apps and design systems with SolidJS. It offers a collection of low-level components and primitives that provide the building blocks for creating a design system from scratch. + +**Accessibility is at the forefront of Kobalte**, making it an ideal option for developers prioritizing inclusive design. + +### Why Choose Kobalte? + +_Kobalte is an excellent choice if accessibility is a priority for your project_. It offers fine-grained component control and a high level of flexibility, ideal for building truly custom user interfaces. + +The unstyled approach allows developers to bring in their preferred styling solutions, whether it's vanilla CSS, Tailwind, or CSS-in-JS. + +### Pros: + +- Focuses on accessibility following [WAI-ARIA Authoring](https://www.w3.org/WAI/ARIA/apg/?ref=yon.fun) Practices. +- Composable components with granular access to component parts. +- Fully unstyled, allowing for complete customization. + +### Cons: + +- Requires more effort for styling since components are unstyled by default. + +## 2\. SUID + +![](https://yon.fun/content/images/2024/11/image-10.png)SUID homepage + +[SUID](https://suid.io/?ref=yon.fun) is a popular UI library built on [Material-UI (MUI)](https://mui.com/?ref=yon.fun) but made to work with SolidJS. + +**It has over 50 components with full TypeScript support and works well with your SolidJS projects.** + +SUID keeps the same style as MUI, which makes it great for developers who have used MUI before but want to work with SolidJS. It also comes with material design themes and is known for its flexibility and modern look. + +### Why Choose SUID? + +If you're used to React and MUI, this library will feel familiar. It provides extensive customization options, making it versatile for many use cases. + +However, if you need a very lightweight solution, SUID might not be the best choice since it can be a bit heavy due to Material-UI features. + +### Pros: + +- Familiar API for developers transitioning from React and MUI. +- Over 50 components with TypeScript support. +- Flexible and modern material design themes. + +### Cons: + +- Inherits some of the complexity and overhead from Material-UI. +- Not the most lightweight solution available. + +## 3\. Solid Bootstrap + +![](https://yon.fun/content/images/2024/11/image-11.png) + +[Solid Bootstrap](https://solid-libs.github.io/solid-bootstrap/?ref=yon.fun) is a version of the popular Bootstrap library for SolidJS. It's great for developers who want to build quickly and without much fuss. + +It has all the classic Bootstrap components like buttons, cards, and modals, and makes them work well with SolidJS. + +### Why Choose Solid Bootstrap? + +This library is ideal for those who prefer the classic Bootstrap look and want to build fast without worrying about complex customizations. + +For example, Solid Bootstrap works well for building administrative dashboards where you need standard UI elements like tables, forms, and navigation bars quickly and easily. + +### Pros: + +- Easy to integrate with existing Bootstrap styles. +- Ideal for developers already familiar with Bootstrap. +- Great for quickly building admin dashboards. + +### Cons: + +- Limited flexibility in customizing beyond Bootstrap's standard components. +- May not be ideal for highly unique or modern UI designs. + +## 4\. Flowbite Solid + +![](https://yon.fun/content/images/2024/11/image-12.png) + +[Flowbite Solid](https://flowbite.com/docs/getting-started/solid-js/?ref=yon.fun) is a set of UI components built with Tailwind CSS and designed for SolidJS. It helps you create clean and responsive designs easily. + +Flowbite components are very customizable and use Tailwind's utility classes, which means you can adjust things like spacing, colors, and fonts easily to make sure your design matches your brand. + +### Why Choose Flowbite Solid? + +If you are a fan of Tailwind CSS and prefer using utility classes to maintain flexibility in styling, Flowbite Solid is a solid option. + +Its responsive and accessible components make it perfect for projects that need a consistent, visually appealing design. + +### Pros: + +- Fully compatible with Tailwind CSS, making it easy to customize. +- Responsive and accessible components. +- Great for building visually appealing, consistent designs. + +### Cons: + +- Requires familiarity with Tailwind CSS for effective use. +- Moderate complexity may not suit quick or simple projects. + +[7 Best Free No-JS Tailwind CSS Component Libraries\\ +\\ +Discover the top 7 lightweight Tailwind CSS component libraries that require no JavaScript. These libraries offer customizable, responsive UI components to streamline your development process and create professional, consistent designs quickly and efficiently.\\ +\\ +![](https://yon.fun/content/images/size/w256h256/2024/03/favicon-60.png)The Art of Dev.Ion Prodan\\ +\\ +![](https://yon.fun/content/images/2024/07/Top-Lightweight-Tailwind-Component-Libraries.png)](https://yon.fun/top-tailwind-component-libs/) + +## 5\. Ark UI + +![](https://yon.fun/content/images/2024/11/image-13.png)Ark UI Homepage + +[Ark UI](https://ark-ui.com/?ref=yon.fun) is a headless UI library, which means it lets you create fully customizable components. It works with many JavaScript frameworks like SolidJS, React, and Vue. + +Ark UI is focused on giving developers lots of flexibility without forcing any specific styles, so you can create unique and accessible components. + +### Why Choose Ark UI? + +If you want full control over how your components look and aim to create something truly unique, Ark UI is a powerful option. + +However, because it is a headless library, it has a steeper learning curve and requires more setup and styling compared to other options. + +### Pros: + +- Full control over styling with headless components. +- Supports multiple frameworks, including SolidJS, React, and Vue. +- Highly customizable and accessible. + +### Cons: + +- Steeper learning curve compared to styled component libraries. +- Requires more effort to style and set up compared to simpler libraries. + +## 6\. SolidUI + +![](https://yon.fun/content/images/2024/11/image-14.png)SolidUI Homepage + +[SolidUI](https://www.solid-ui.com/?ref=yon.fun) gives you a set of nice-looking components that you can easily use in your SolidJS projects. It is easy to customize and is perfect for making quick prototypes or simple web interfaces without much effort. + +SolidUI is also an unofficial port of [shadcn/ui](https://ui.shadcn.com/?ref=yon.fun) to SolidJS and is not affiliated with @shadcn. + +When working with SolidJS and libraries like Solid-UI, you'll often deal with reactive components. + +```ts +const [count, setCount] = createSignal(0); +``` + +If you need to organize and snapshot these code pieces efficiently, I recommend [SnipsCo](https://snipsco.com/?ref=yon.fun) \- it's great for developers managing multiple UI snippets. + +### Why Choose SolidUI? + +If you want a simple and fast solution with ready-made components, SolidUI is an excellent option. + +Compared to Solid Bootstrap, SolidUI is geared towards providing out-of-the-box components, helping you start building immediately without spending time on extra customization. + +As it is an unofficial port of shadcn/ui, it offers a similar experience for developers already familiar with shadcn. + +### Pros: + +- Easy drag-and-drop integration. +- Customizable components that work well without much effort. +- Great for quick prototyping. + +### Cons: + +- Limited customization compared to headless or more complex libraries. +- Not ideal for larger, complex projects that need advanced functionality. + +## 7\. shadcn-solid + +![](https://yon.fun/content/images/2024/11/image-15.png) + +[shadcn-solid](https://shadcn-solid.com/?ref=yon.fun) is also an unofficial version of the popular [shadcn/ui library](https://ui.shadcn.com/?ref=yon.fun) made for SolidJS. + +It has lots of customizable and accessible components that are easy to add to your SolidJS apps. The goal of this library is to give developers a smooth experience by providing consistent design and tools that are easy to use. + +### Why Choose shadcn-solid? + +If you like the design and usability of shadcn/ui and wish to adapt it for SolidJS, this library is ideal. + +It works well for projects that need a consistent design across different platforms, providing accessible and easily customizable components. + +### Pros: + +- Based on the popular shadcn/ui, making it familiar to many developers. +- Components are accessible and easy to customize. +- Suitable for projects needing a consistent design across different platforms. + +### Cons: + +- Less feature-rich compared to some other medium or high-complexity libraries. +- Customization is easier, but can still be limited by the library's design choices. + +## 8\. Corvu + +![](https://yon.fun/content/images/2024/11/image-16.png)Corvu Homepage + +[Corvu](https://corvu.dev/?ref=yon.fun) is a collection of basic UI building blocks for SolidJS. Unlike other libraries, **Corvu does not come with pre-styled components**, which means you have full control over the look and feel of your app. + +It's perfect if you want to create something truly custom and don't mind doing all the styling work yourself. + +### Why Choose Corvu? + +If you want to create your UI from scratch and have full control over every detail, Corvu is the perfect tool. + +It's great for developers who are comfortable doing their styling and want to create a highly unique, custom UI. + +### Pros: + +- Provides maximum control by offering basic UI building blocks. +- Perfect for creating highly unique and custom UIs. +- Focused on accessibility and developer flexibility. + +### Cons: + +- Requires developers to handle all aspects of styling. +- Not suitable for those who need ready-to-use components. + +## Comparison Table of SolidJS UI Libraries + +| Library Name | Complexity | Key Features | Best Use Case | +| --- | --- | --- | --- | +| Kobalte | Medium | Accessible, composable, unstyled | Building accessible and fully custom UIs | +| SUID | Medium | TypeScript support, Material-UI API | Transitioning from React to SolidJS | +| Solid Bootstrap | Low | Bootstrap components, easy integration | Quick dashboards and admin panels | +| Flowbite Solid | Medium | Tailwind CSS integration, responsive components | Projects using Tailwind CSS for styling | +| Ark UI | High | Headless components, full customization | Unique, highly customized UI projects | +| SolidUI | Low | Pre-designed components, easy setup | Rapid prototyping and simple web interfaces | +| shadcn-solid | Medium | Based on shadcn/ui, customizable components | Consistent design across platforms | +| Corvu | High | UI primitives, no default styles | Highly custom, unique styling from scratch | + +_Table 1: Discover the best UI libraries for SolidJS, comparing complexity, key features, and the best use cases to help you choose the right tool for your project._ + +## Conclusion + +Choosing the right UI library for your SolidJS project depends on what you need: _speed, customization, or something in between._ + +[Kobalte](https://yon.fun/solidjs-ui-libs/#1-kobalte) is ideal if accessibility and composability are crucial, [SUID](https://yon.fun/solidjs-ui-libs/#2-suid) is great if you're used to React, [Solid Bootstrap](https://yon.fun/solidjs-ui-libs/#3-solid-bootstrap) is perfect if you like Bootstrap, and [Flowbite Solid](https://yon.fun/solidjs-ui-libs/#4-flowbite-solid) is ideal if you love Tailwind CSS. + +For maximum control, [**Ark UI**](https://yon.fun/solidjs-ui-libs/#5-ark-ui) **and** [**Corvu**](https://yon.fun/solidjs-ui-libs/#8-corvu) **are good choices because they let you design however you want.** + +If you like shadcn/ui, [shadcn-solid](https://yon.fun/solidjs-ui-libs/#7-shadcn-solid), and [SolidUI](https://yon.fun/solidjs-ui-libs/#6-solidui) are a great version of SolidJS. + +We hope this guide helps you pick the best UI library for your next SolidJS project! + +## FAQs: + +### Which SolidJS UI library is best for beginners? + +[SolidUI](https://yon.fun/solidjs-ui-libs/#6-solidui) and [Solid Bootstrap](https://yon.fun/solidjs-ui-libs/#3-solid-bootstrap) are ideal for beginners because they are easy to use and don't require a lot of custom styling. + +### Can I use Bootstrap with SolidJS? + +Yes, you can use Solid Bootstrap, which is a version of the popular Bootstrap library adapted for SolidJS. + +### What are the benefits of using Ark UI for SolidJS? + +Ark UI allows for complete customization, making it suitable for projects where you need a unique look and want full control over component styling. + +Feel free to check out our further reading on SolidJS best practices to take your development to the next level. \ No newline at end of file diff --git a/.firecrawl/stanza-compound-components.md b/.firecrawl/stanza-compound-components.md new file mode 100644 index 0000000..e3561f9 --- /dev/null +++ b/.firecrawl/stanza-compound-components.md @@ -0,0 +1,536 @@ +Compound components let a group of related components share implicit state through Context, giving consumers full control over layout and composition. Think of how `'); + return ctx; +} + +function Select({ children, onChange }: { children: ReactNode; onChange?: (v: string) => void }) { + const [isOpen, setOpen] = useState(false); + const [selectedValue, setSelectedValue] = useState(null); + const [highlightedIndex, setHighlightedIndex] = useState(0); + const [options, setOptions] = useState([]); + + const registerOption = (value: string) => { + setOptions(prev => prev.includes(value) ? prev : [...prev, value]); + }; + + const select = (value: string) => { + setSelectedValue(value); + setOpen(false); + onChange?.(value); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') setHighlightedIndex(i => Math.min(i + 1, options.length - 1)); + if (e.key === 'ArrowUp') setHighlightedIndex(i => Math.max(i - 1, 0)); + if (e.key === 'Enter' && isOpen) select(options[highlightedIndex]); + if (e.key === 'Escape') setOpen(false); + }; + + return ( + +
+ {children} +
+
+ ); +} + +function Trigger({ children, placeholder }: { children?: ReactNode; placeholder?: string }) { + const { isOpen, setOpen, selectedValue } = useSelect(); + return ( + + ); +} + +function Options({ children }: { children: ReactNode }) { + const { isOpen } = useSelect(); + if (!isOpen) return null; + return
    {children}
; +} + +function Option({ value, children }: { value: string; children: ReactNode }) { + const { select, selectedValue, registerOption } = useSelect(); + useEffect(() => { registerOption(value); }, [value]); + return ( +
  • select(value)} + className={selectedValue === value ? 'option selected' : 'option'} + > + {children} +
  • + ); +} + +Select.Trigger = Trigger; +Select.Options = Options; +Select.Option = Option; + +// Usage + +``` + +A Select component demonstrates a more complex compound pattern. The parent manages open state, selection, keyboard navigation, and an option registry. Each Option registers itself on mount so the parent knows about all available options for keyboard navigation. This is a simplified version of what libraries like Radix UI and Headless UI ship. + +### Modal with Header, Body, Footer slots + +tsxCopy + +``` +import { createContext, useContext, ReactNode, useEffect } from 'react'; +import { createPortal } from 'react-dom'; + +type ModalCtx = { + onClose: () => void; +}; + +const ModalCtx = createContext(null); + +function useModal() { + const ctx = useContext(ModalCtx); + if (!ctx) throw new Error('Modal.* must be used within '); + return ctx; +} + +type ModalProps = { + isOpen: boolean; + onClose: () => void; + children: ReactNode; +}; + +function Modal({ isOpen, onClose, children }: ModalProps) { + useEffect(() => { + if (!isOpen) return; + const handleEscape = (e: KeyboardEvent) => { + if (e.key === 'Escape') onClose(); + }; + document.addEventListener('keydown', handleEscape); + document.body.style.overflow = 'hidden'; + return () => { + document.removeEventListener('keydown', handleEscape); + document.body.style.overflow = ''; + }; + }, [isOpen, onClose]); + + if (!isOpen) return null; + + return createPortal( + +
    +
    e.stopPropagation()} + > + {children} +
    +
    +
    , + document.body + ); +} + +function Header({ children }: { children: ReactNode }) { + const { onClose } = useModal(); + return ( +
    + {children} + +
    + ); +} + +function Body({ children }: { children: ReactNode }) { + return
    {children}
    ; +} + +function Footer({ children }: { children: ReactNode }) { + return
    {children}
    ; +} + +Modal.Header = Header; +Modal.Body = Body; +Modal.Footer = Footer; + +// Usage + setShowConfirm(false)}> +

    Delete project?

    + +

    This will permanently delete {project.name} and all its data.

    +
    + + + + +
    +``` + +The Modal shares onClose through Context so the Header can render a close button without the consumer wiring it up. The Footer is optional — if you only need a confirmation message, skip it entirely. Portals ensure the modal escapes parent overflow and z-index stacking. This pattern shows compound components working beyond just state sharing — they also provide structural conventions. + +### Controlled mode — letting parents own the state + +tsxCopy + +``` +type TabsProps = { + // Uncontrolled: component owns state + defaultTab?: string; + // Controlled: parent owns state + activeTab?: string; + onTabChange?: (id: string) => void; + children: ReactNode; +}; + +function Tabs({ defaultTab, activeTab: controlledTab, onTabChange, children }: TabsProps) { + const [internalTab, setInternalTab] = useState(defaultTab ?? ''); + + const isControlled = controlledTab !== undefined; + const activeTab = isControlled ? controlledTab : internalTab; + + const setActiveTab = (id: string) => { + if (!isControlled) setInternalTab(id); + onTabChange?.(id); + }; + + return ( + +
    {children}
    +
    + ); +} + +// Uncontrolled — fire and forget + + + Overview + API + + ... + ... + + +// Controlled — parent syncs with URL params +const [tab, setTab] = useState(searchParams.get('tab') ?? 'overview'); + + { + setTab(id); + setSearchParams({ tab: id }); +}}> + {/* same children */} + +``` + +Supporting both modes is essential for real-world compound components. Uncontrolled mode is convenient for static UIs. Controlled mode is necessary when the active state needs to sync with URL parameters, form state, or other components. The pattern checks whether a controlled value is provided and delegates accordingly. + +## Common Mistakes + +Mistake: + +Using \`React.Children.map\` and \`cloneElement\` to inject props into children instead of using Context + +Fix: + +Context works regardless of nesting depth and does not break when consumers wrap sub-components in divs, fragments, or custom wrappers. cloneElement only works on direct children and silently fails when the tree structure changes. Context is the correct primitive for compound components. + +Mistake: + +Failing to throw an error when a sub-component is used outside its parent provider — returning null or using a default value instead + +Fix: + +Always throw a descriptive error in the custom hook: \`throw new Error('Tabs.Tab must be used within ')\`. Silent failures lead to hours of debugging. The developer needs to know immediately that they placed a component in the wrong part of the tree. + +Mistake: + +Putting too much into the Context value — exposing internal state, dispatch functions, and derived values that sub-components never use + +Fix: + +Keep the context value minimal. If some sub-components need data that others do not, consider splitting into two contexts (one for state, one for dispatch) or providing only what the public sub-components actually read. A bloated context causes unnecessary re-renders across all consumers. + +Mistake: + +Creating a new context value object on every render, which triggers re-renders in all consumers even when the actual state has not changed + +Fix: + +Memoize the context value with \`useMemo\`: \`const value = useMemo(() => ({ activeTab, setActiveTab }), \[activeTab\])\`. This ensures consumers only re-render when the state they depend on actually changes. + +## Best Practices + +- Use the dot notation pattern (\`Tabs.Tab\`, \`Tabs.Panel\`) to attach sub-components to the parent. This makes the API self-documenting — autocomplete shows all available sub-components when you type \`Tabs.\` +- Support both controlled and uncontrolled modes. Accept \`defaultValue\` for uncontrolled usage and \`value\` + \`onChange\` for controlled usage. Check if the controlled prop is defined to determine which mode to use. +- Memoize the Context value with \`useMemo\` to prevent unnecessary re-renders. This matters especially when the parent component has props or state unrelated to the compound component's shared state. +- Write a custom hook (e.g., \`useTabs\`, \`useAccordion\`) that throws on null context instead of using \`useContext\` directly in sub-components. This centralizes error handling and gives you a single place to add logging or validation. +- Keep sub-components focused on one responsibility. The Trigger handles click events, the Content handles visibility, the Item provides grouping. Do not merge responsibilities — it defeats the composability that makes this pattern valuable. + +## Summary + +Compound components are a group of related React components that share implicit state through Context, giving consumers full control over markup and layout. The parent component provides state via a Context Provider, and sub-components consume it through a custom hook. This pattern replaces sprawling props-based APIs with a composable, declarative interface. Attach sub-components using dot notation for discoverability, throw clear errors when components are used outside their parent, memoize the context value, and support both controlled and uncontrolled modes. Every serious React component library — Radix, Headless UI, Ark UI — builds on this pattern. + +## Go deeper with Stanza + +Practice react compound components with interactive lessons and challenges. + +[React Components & Patterns](https://www.stanza.dev/courses/react-components-patterns) [React 19 & Patterns](https://www.stanza.dev/courses/react-modern-patterns) + +## Related Concepts + +[React Context Api](https://www.stanza.dev/concepts/react-context-api "React Context Api") [React Custom Hooks](https://www.stanza.dev/concepts/react-custom-hooks "React Custom Hooks") [React Usestate](https://www.stanza.dev/concepts/react-usestate "React Usestate") [React Error Boundaries](https://www.stanza.dev/concepts/react-error-boundaries "React Error Boundaries") + +## Related Cheatsheets + +[React Hooks Cheatsheet](https://www.stanza.dev/cheatsheet/react-hooks "React Hooks Cheatsheet") \ No newline at end of file diff --git a/.firecrawl/state-of-react-2025.md b/.firecrawl/state-of-react-2025.md new file mode 100644 index 0000000..430f6c0 --- /dev/null +++ b/.firecrawl/state-of-react-2025.md @@ -0,0 +1,1027 @@ +[Skip to content](https://2025.stateofreact.com/en-US/libraries/component-libraries/#page-main) + +[« Libraries](https://2025.stateofreact.com/en-US/libraries/) + +English + +[State Management »](https://2025.stateofreact.com/en-US/libraries/state-management/) + +## UI Libraries + +The building blocks of your front-end + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=component_libraries_ratios¶ms=§ionId=libraries&subSectionId=component_libraries) UI Libraries Ratios Over Time + +While **MUI** (a.k.a. Material UI) is still number one in terms of usage, **shadcn/ui** is growing fast and on the verge of overtaking it for the top spot. + +All Respondents + +Query Builder… + +Ant Design + +Chakra UI + +Headless UI + +MUI + +Radix + +React Bootstrap + +shadcn/ui + +React Aria + +Mantine + +Magic UI + +HeroUI (previously NextUI) + +#### Mode: + +ValueRank + +#### View: + +UsageAwarenessInterestSatisfactionAppreciationPositivity + +#### Usage: + +Proportion of respondents **having used** an item + +0% + +20% + +40% + +60% + +80% + +100% + +2023 + +2024 + +2025 + +27%25%28%22%20%21%22%21%22%56%56%56%25%36%47%32%30%30%20%42%56%10%16%20%11%13%3%6% + +0% + +20% + +40% + +60% + +80% + +100% + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=component_libraries_cardinalities¶ms=§ionId=libraries&subSectionId=component_libraries) Number of Items + +A big proportion of users don't use any component library at all–while at the same time, those who _do_ use one are likely to have tried out multiple options. This suggests the space isn't quite settled yet, and that there's still room for new entrants to make their mark. + +All Respondents + +Query Builder… + +0% + +7% + +13% + +20% + +27% + +33% + +Average: **2.3** + +01 + +0 + +1,246 + +02 + +1 + +473 + +03 + +2 + +523 + +04 + +3 + +501 + +05 + +4 + +370 + +06 + +5 + +260 + +07 + +6 + +171 + +08 + +7 + +121 + +09 + +8 + +44 + +10 + +9 + +34 + +11 + +10 + +7 + +12 + +11 + +10 + +0% + +7% + +13% + +20% + +27% + +33% + +% of survey respondents + +2.3 + +Share Chart…Export Data… + +💡 + +You can click on any technology name to get extra details and a more in-depth look at its related data. + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=component_libraries_others¶ms=§ionId=libraries&subSectionId=component_libraries) Other UI Libraries + +Comments for “Other UI Libraries” 2 + +**Base UI** is another entrant in the very competitive space of headless UI libraries–in other words UI libraries that provide component behavior, but leave styling up to you. And with many **Radix** veterans as part of the team, it's definitely worth checking out if that's what you're after. + +What other component libraries have you used? + +Freeform + +Multiple + +All Respondents + +Query Builder… + +0% + +20% + +40% + +60% + +80% + +100% + +01 + +[Base UI](https://base-ui.com/) + +Answers matching “Base UI” 49 + +02 + +daisyUI + +Answers matching “daisyUI” 19 + +03 + +[Fluent UI](https://developer.microsoft.com/en-us/fluentui#/) + ++5 + +Answers matching “Fluent UI” 9 + +04 + +[Ark UI](https://ark-ui.com/%20-%20react%20-%20ui_libraries) + ++9 + +Answers matching “Ark UI” 9 + +05 + +[PrimeReact](https://primereact.org/) + ++4 + +Answers matching “PrimeReact” 8 + +06 + +Material UI + +Answers matching “Material UI” 8 + +07 + +Tailwind CSS + +-5 + +Answers matching “Tailwind CSS” 7 + +08 + +[shadcn/ui](https://ui.shadcn.com/) + +Answers matching “shadcn/ui” 7 + +09 + +[Ariakit](https://ariakit.org/) + +-4 + +Answers matching “Ariakit” 7 + +10 + +[Tamagui](https://tamagui.dev/) + +-4 + +Answers matching “Tamagui” 6 + +11 + +Semantic UI/Fomantic UI + +-4 + +Answers matching “Semantic UI/Fomantic UI” 6 + +12 + +Styled Components + +Answers matching “Styled Components” 5 + +13 + +Other Answers + +Answers matching “Other Answers” 140 + +0% + +20% + +40% + +60% + +80% + +100% + +% of question respondents + +221 + +6% + +5 + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=animation_libraries¶ms=§ionId=libraries&subSectionId=component_libraries) Animation + +Comments for “Animation” 2 + +Not every project needs fancy animations, but for those who do **Motion** has established itself as a serious option. + +Which animation libraries have you used? + +Multiple + +All Respondents + +Query Builder… + +0% + +20% + +40% + +60% + +80% + +100% + +1 + +[Motion (ex-Framer Motion)](https://motion.dev/) + +1,443 + +2 + +🚫 None + ++2 + +741 + +3 + +[React-Spring](https://react-spring.dev/) + +631 + +4 + +GSAP + +588 + +5 + +React Motion + +504 + +6 + +React Native Reanimated + +365 + +7 + +AutoAnimate + +76 + +8 + +Anime.js + ++2 + +Answers matching “Anime.js” 10 + +9 + +Other Answers + +33 + +0% + +20% + +40% + +60% + +80% + +100% + +% of question respondents + +2,631 + +70% + +10 + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=data_visualization_libraries¶ms=§ionId=libraries&subSectionId=component_libraries) Data Visualization + +Comments for “Data Visualization” 4 + +Despite not being a React-specific chart library, **Chart.js** still tops the rankings. + +Want to guess what library _we_ use for the charts you see here? Trick question, they're all hand-coded from scratch! + +Which data visualization and graphics libraries have you used? + +Multiple + +All Respondents + +Query Builder… + +0% + +20% + +40% + +60% + +80% + +100% + +01 + +[Chart.js](https://www.chartjs.org/) + +1,245 + +02 + +D3 + +1,037 + +03 + +Recharts + +969 + +04 + +🚫 None + ++3 + +460 + +05 + +[Highcharts](https://www.highcharts.com/) + +437 + +06 + +[ECharts](https://echarts.apache.org/en/index.html) + ++5 + +207 + +07 + +[ApexCharts](https://apexcharts.com/) + ++5 + +174 + +08 + +Nivo + +-2 + +154 + +09 + +[Victory](https://formidable.com/open-source/victory/) + +-2 + +128 + +10 + +Visx + +-1 + +120 + +Show All (5 hidden) + +0% + +20% + +40% + +60% + +80% + +100% + +% of question respondents + +2,583 + +69% + +10 + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=form_libraries¶ms=§ionId=libraries&subSectionId=component_libraries) Form Libraries + +Comments for “Form Libraries” 11 + +Nobody likes dealing with forms, but when you can't help it **React Hook Forms** at least makes the whole thing a lot less painful. + +Which libraries have you used to manage forms? + +Multiple + +All Respondents + +Query Builder… + +0% + +20% + +40% + +60% + +80% + +100% + +01 + +React Hook Form + +1,964 + +02 + +Formik + +1,095 + +03 + +[TanStack Form](https://tanstack.com/form/latest) + ++8 + +567 + +04 + +🚫 None + ++2 + +355 + +05 + +Redux Form + +-1 + +260 + +06 + +React Final Form + +-1 + +164 + +07 + +[Mantine Forms](https://mantine.dev/form/use-form/) + +-1 + +122 + +08 + +react-jsonschema-form + +-1 + +62 + +09 + +Conform + +-1 + +56 + +10 + +@formily/core + +-1 + +19 + +11 + +Other Answers + +42 + +0% + +20% + +40% + +60% + +80% + +100% + +% of question respondents + +2,643 + +70% + +10 + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=react_form_patterns¶ms=§ionId=libraries&subSectionId=component_libraries) Form Patterns + +Comments for “Form Patterns” 7 + +The debate between controlled and uncontrolled form components isn't really one. Respondents mainly switch between either pattern; but if they need to pick then controlled forms are the clear winner. + +Which form pattern do you usually prefer using? + +All Respondents + +Query Builder… + +0% + +20% + +40% + +60% + +80% + +100% + +1 + +Both, depending on the situation + +1,423 + +2 + +Controlled form components + +1,024 + +3 + +Uncontrolled form components + +200 + +4 + +Other Answers + +8 + +0% + +20% + +40% + +60% + +80% + +100% + +% of question respondents + +2,647 + +70% + +10 + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=css_tools¶ms=§ionId=libraries&subSectionId=component_libraries) CSS Tools & Libraries + +Comments for “CSS Tools & Libraries” 8 + +CSS-in-JS had its 15 minutes of fame, but the appearance of **Tailwind**, as well as the huge improvements in CSS itself in the last couple years, have put a big dent in its popularity. + +Which libraries or frameworks have you used to style your React apps? + +Multiple + +All Respondents + +Query Builder… + +0% + +20% + +40% + +60% + +80% + +100% + +01 + +Tailwind CSS + +2,142 + +02 + +CSS Modules + +1,794 + +03 + +Sass/SCSS + +1,607 + +04 + +Styled Components + +1,594 + +05 + +Emotion + +786 + +06 + +Styled JSX + +467 + +07 + +Less + ++4 + +329 + +08 + +vanilla-extract + +-1 + +199 + +09 + +[Panda](https://panda-css.com/) + +-1 + +103 + +10 + +Stitches + +-1 + +102 + +Show All (5 hidden) + +0% + +20% + +40% + +60% + +80% + +100% + +% of question respondents + +2,740 + +73% + +10 + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=component_libraries_pain_points¶ms=§ionId=libraries&subSectionId=component_libraries) Component Pain Points + +Developers seem to have a love/hate relationship with **Tailwind**, with some citing it as a pain point while others see it as the answer to their UI troubles. + +What pain points have you encountered related to working with components? + +Freeform + +Multiple + +All Respondents + +Query Builder… + +0% + +20% + +40% + +60% + +80% + +100% + +1 + +Styling & customization + +Answers matching “Styling & customization” 34 + +2 + +Tailwind CSS + +Answers matching “Tailwind CSS” 17 + +3 + +React issues + +Answers matching “React issues” 13 + +4 + +CSS-in-JS issues + ++1 + +Answers matching “CSS-in-JS issues” 13 + +5 + +Form issues + +-3 + +Answers matching “Form issues” 9 + +6 + +Excessive complexity + ++1 + +Answers matching “Excessive complexity” 6 + +7 + +Breaking changes + +Answers matching “Breaking changes” 5 + +8 + +Bloat + ++9 + +Answers matching “Bloat” 5 + +9 + +Other Answers + +Answers matching “Other Answers” 105 + +0% + +20% + +40% + +60% + +80% + +100% + +% of question respondents + +115 + +3% + +5 + +Share Chart…Export Data… + +### [Link to section](https://share.devographics.com/share/prerendered?localeId=en-US&surveyId=state_of_react&editionId=react2025&blockId=component_libraries_resources¶ms=§ionId=libraries&subSectionId=component_libraries) Recommended Resources + +[![Enterprise UI Development: Testing & Code Quality ](https://static.frontendmasters.com/assets/teachers/kinney/thumb@2x.webp)](https://frontendmasters.com/courses/enterprise-ui-dev/?utm_source=stateofreact&utm_medium=website&utm_campaign=stateofreact2025&utm_content=textlink "Enterprise UI Development: Testing & Code Quality ") + +Steve Kinney + +Temporal + +#### [Enterprise UI Development: Testing & Code Quality](https://frontendmasters.com/courses/enterprise-ui-dev/?utm_source=stateofreact&utm_medium=website&utm_campaign=stateofreact2025&utm_content=textlink) + +Learn unit testing with Vitest, continuous integration via GitHub Actions, component and accessibility testing with Axe, mocking techniques, and code standard enforcement using ESLint and Husky & Lint-Staged. + +[![The Hard Parts of UI Development ](https://static.frontendmasters.com/assets/teachers/sentance/thumb@2x.webp)](https://frontendmasters.com/courses/hard-parts-ui-dev/?utm_source=stateofreact&utm_medium=website&utm_campaign=stateofreact2025&utm_content=textlink "The Hard Parts of UI Development ") + +Will Sentance + +Codesmith + +#### [The Hard Parts of UI Development](https://frontendmasters.com/courses/hard-parts-ui-dev/?utm_source=stateofreact&utm_medium=website&utm_campaign=stateofreact2025&utm_content=textlink) + +Develop an under-the-hood knowledge of UI dev by learning techniques such as data binding, UI composition, templating, virtual DOM and its reconciliation, and hooks, all from scratch! + +Sponsored content from our partners. [Learn more.](https://2025.stateofreact.com/en-US/support/) \ No newline at end of file diff --git a/.firecrawl/ui-lab-ai-integration.md b/.firecrawl/ui-lab-ai-integration.md new file mode 100644 index 0000000..f7b50e7 --- /dev/null +++ b/.firecrawl/ui-lab-ai-integration.md @@ -0,0 +1,239 @@ +#### AI integration + +Specifications and patterns for integrating AI capabilities with UI Lab components. + +## Accessing LLMs.txt + +After installing UI Lab, you can reference the documentation in multiple ways: + +### Method 1: Local file + +The LLMs.txt file is included in the package. Reference it from your node\_modules: + +``` +node_modules/@ui-lab/core/LLMs.txt +``` + +### Method 2: CLI + +Print LLMs.txt content to the terminal: + +``` +npx ui-lab llms +``` + +### Method 3: Web documentation + +Visit the UI Lab website for interactive component reference documentation. + +## Using with AI tools + +### ChatGPT / Claude / Copilot + +Provide the LLMs.txt content when asking AI tools to generate components. Copy and paste the documentation at the start of your conversation: + +``` +You are a React/TypeScript developer. Use UI Lab components for the UI layer. + +Here is the complete component documentation: + +[Copy LLMs.txt content here] + +Now, build a user profile card that shows: +- User avatar +- User name and email +- Edit and delete buttons +- Responsive design +``` + +The AI will generate code using the documented components and patterns, respecting the design system constraints. + +### IDE Extensions + +Use GitHub Copilot or Cursor with the documentation. Add a comment with instructions: + +``` +// Use UI Lab components +// Button variants: primary, secondary, tertiary, destructive +// Card has Header, Title, Description, Content, Footer slots +// Build a login form with email, password, and submit button + +export default function LoginForm() { + // IDE suggests code using UI Lab components +} +``` + +## Example prompts + +### Simple component + +Start simple. Provide clear requirements and let the AI handle implementation: + +``` +Using UI Lab components, build a settings card with: +- Title: "Display Settings" +- Toggle for dark mode +- Dropdown for language selection +- Save button at the bottom + +Make it accessible with proper labels and ARIA attributes. +``` + +### Complex feature + +For larger features, break down requirements and explain data structure: + +``` +Build a product list component using UI Lab. Requirements: + +Data: +- products: { id, name, price, category, inStock }[] +- isLoading: boolean +- error: string | null + +Features: +- Display products in a responsive grid +- Show product card with name, price, category badge +- Disable purchase button if out of stock +- Show loading state with skeleton cards +- Display error message if loading fails +- Include pagination (10 items per page) + +Use Tailwind classes for layout, UI Lab for components. +``` + +### Style and behavior specifics + +Be specific about behavior and styling to get better results: + +``` +Build a notification component using UI Lab: + +Requirements: +- Type: success, error, warning, info +- Auto-dismiss after 5 seconds +- Allow manual close +- Stack multiple notifications vertically +- Show icon based on type +- Use proper semantic colors (success-500, destructive-500, etc) +- Animate in/out smoothly +- Position fixed at top-right +- Respond to keyboard (Escape to close) +``` + +## Best practices for AI code generation + +### 1\. Provide context + +Give the AI information about your project structure, state management (React hooks, Redux, etc), and any existing patterns. This helps generate code that fits your codebase. + +### 2\. Include LLMs.txt early + +Always provide the LLMs.txt documentation in the first message or system prompt. This prevents the AI from inventing components or props that don't exist. + +### 3\. Review generated code + +Even with AI guidance, review generated code for accessibility, performance, and correctness. Check for proper ARIA attributes, keyboard navigation, and semantic HTML. + +### 4\. Test accessibility + +Run accessibility checks on generated code. Use tools like axe DevTools, Lighthouse, or WebAIM to ensure components are truly accessible. + +### 5\. Iterate with feedback + +Provide feedback to the AI when code doesn't meet requirements. Iterate by pointing out issues and asking for adjustments rather than starting over. + +### 6\. Don't bypass documentation + +If the AI generates props or components that seem wrong, check the LLMs.txt documentation. The AI's understanding is only as good as the documentation provided. + +## Understanding LLMs.txt structure + +LLMs.txt is organized by section, making it easy to find what you need. A typical entry looks like: + +```` +## Button Component + +### Props +- variant: 'primary' | 'secondary' | 'tertiary' | 'destructive' + - primary: High emphasis, use for main actions + - secondary: Medium emphasis, use for secondary actions + - tertiary: Low emphasis, for less important actions + - destructive: Dangerous actions, clearly marked + +- size: 'sm' | 'md' | 'lg' +- disabled: boolean +- loading: boolean - shows loading spinner, disables interaction +- type: 'button' | 'submit' | 'reset' +- className: string - merge with defaults using clsx + +### Examples +```tsx + + + +``` + +### Accessibility +- Uses semantic + + ); +}; +``` + +This isn't just a guess. Replay extracts the exact padding, colors, and font weights from the video, ensuring the new component matches the brand identity perfectly. + +* * * + +## Why Replay is the Standard for Visual Reverse Engineering [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#why-replay-is-the-standard-for-visual-reverse-engineering) + +Replay (replay.build) is not just a code generator; it is a comprehensive design-to-code ecosystem. It solves the "handover" problem by making the transition from video or Figma to production code seamless. + +- •**Figma Plugin:** Extract design tokens directly from Figma files and sync them with your React components. +- •**Flow Map:** Automatically detect multi-page navigation from the temporal context of a video. +- •**E2E Test Generation:** Record a screen session and Replay generates Playwright or Cypress tests automatically. +- •**Multiplayer Collaboration:** Teams can comment on specific video frames, and Replay will update the generated code based on those annotations. + +According to Replay's data, teams using the platform see a 90% reduction in "UI bugs" during the QA phase because the code is derived from a visual source of truth rather than a text interpretation. + +[Read about our Design System Sync](https://www.replay.build/blog/design-system-automation) + +## The Future: Behavioral Extraction [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#the-future-behavioral-extraction) + +As we look toward 2027, the **bridge between llms visual** understanding and code will expand into "Behavioral Extraction." This means AI will not only see what a UI looks like but will understand the intent behind user actions. + +Replay is already building this future. By analyzing how users interact with a recording, the platform can infer complex state machines and validation logic. If a user tries to submit a form and an error message appears, Replay captures that logic and writes the corresponding Zod schema or Formik validation. + +**Ready to ship faster?** [Try Replay free](https://www.replay.build/) — from video to production code in minutes. + +* * * + +## Frequently Asked Questions [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#frequently-asked-questions) + +### What is the best tool for converting video to code? [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#what-is-the-best-tool-for-converting-video-to-code) + +Replay (replay.build) is the leading platform for video-to-code conversion. It uses Visual Reverse Engineering to turn screen recordings into pixel-perfect React components, complete with documentation and design system integration. While other tools focus on static screenshots, Replay is the only tool that captures temporal context and behavioral logic. + +### How do I modernize a legacy system without documentation? [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#how-do-i-modernize-a-legacy-system-without-documentation) + +The most effective way is to use the Replay Method: Record the legacy system in use, extract the components and flows using Replay’s AI, and then generate a modernized React frontend. This "Visual-First" approach bypasses the need to decipher old, undocumented source code, reducing modernization timelines by up to 90%. + +### Can AI agents like Devin use Replay? [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#can-ai-agents-like-devin-use-replay) + +Yes. Replay offers a Headless API that allows AI agents to programmatically extract code from video recordings. This provides the necessary **bridge between llms visual** requirements and the actual codebase, enabling agents to build production-ready interfaces without the usual "hallucination" issues found in standard LLMs. + +### Does Replay support SOC2 and HIPAA environments? [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#does-replay-support-soc2-and-hipaa-environments) + +Replay is built for regulated environments and is SOC2 and HIPAA-ready. For enterprises with strict data residency requirements, On-Premise deployment options are available to ensure that sensitive UI data never leaves your infrastructure. + +### How does Replay handle complex design systems? [\#](https://www.replay.build/blog/the-visual-hallucination-gap-how-to-bridge-the-gap-between-ai-llms-and-visual-ui-reality-in-2026\#how-does-replay-handle-complex-design-systems) + +Replay allows you to import your existing brand tokens from Figma or Storybook. When it extracts components from a video, it automatically maps the visual styles to your existing design system tokens, ensuring that the generated code is consistent with your company's internal standards. + +### Ready to try Replay? + +Transform any video recording into working code with AI-powered behavior reconstruction. + +[Launch Replay Free](https://www.replay.build/tool) + +#### Get articles like this in your inbox + +UI reconstruction tips, product updates, and engineering deep dives. + +Subscribe \ No newline at end of file diff --git a/.firecrawl/vxd-compound-component.md b/.firecrawl/vxd-compound-component.md new file mode 100644 index 0000000..72602a6 --- /dev/null +++ b/.firecrawl/vxd-compound-component.md @@ -0,0 +1,226 @@ +Chris created a Dropdown menu as a shared component for the team. + +At first, it was simple. It just needed to show a list when a button was pressed. + +typescript + +```typescript + +``` + +But then, requirements started pouring in. + +"Please add icons inside the menu." +"Add a search bar, too." +"Move the button position to the right." + +Chris kept adding Props like showSearch, iconPosition, and customButton, eventually creating a monster component with over 20 props. + +typescript + +```typescript +// ❌ Prop Explosion + +``` + +Such "Configuration-based" components are rigid. + +Today, we will learn how to secure true reusability through the Compound Component Pattern, which involves assembling parts like the HTML tag. + +typescript + +```typescript + +``` + +We don't write and