Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ See the [docs site](https://uibuilder.app/) for more information.

---

## Tailwind 4, React 19 Support and latest Shadcn/ui
## Compatibility Notes

Migration will be coming soon. Some 3rd party shadcn component dependencies are not yet compatible with the latest versions of Tailwind, so we are waiting for the latest versions of these components to be stable.
If you are having issues with latest shadcn/ui cli you can try using older version in the command like `npx [email protected] add ...`
**Tailwind 4 + React 19**: Migration coming soon. Currently blocked by 3rd party component compatibility. If using latest shadcn/ui CLI fails, try: `npx [email protected] add ...`

**Server Components**: Not supported. RSC can't be re-rendered client-side for live preview. A separate RSC renderer for final page rendering is possible — open an issue if you have a use case.


# Quick Start
Expand Down Expand Up @@ -496,7 +497,7 @@ Separate content from structure, allowing non-technical users to update dynamic
---


## Changelog
## Breaking Changes

### v1.0.0
- Removed _page_ layer type in favor of using any component type (like `div`, `main`, or custom containers) as the root page layer. This enhances flexibility, enabling use cases like building react-email templates directly. You should migrate any layers stored in the database to use a standard component type as the root. The [migrateV2ToV3](lib/ui-builder/store/layer-utils.ts) function in `layer-utils.ts` can assist with this migration.
Expand Down Expand Up @@ -537,6 +538,9 @@ npm run test

## Roadmap

- [ ] Config options to make pages and variables immutable
- [ ] Add variable binding to layer children and not just props
- [ ] Improve DX. End to end type safety.
- [ ] Documentation site for UI Builder with more hands-on examples
- [ ] Configurable Tailwind Class subset for things like React-Email components
- [ ] Drag and drop component in the editor panel and not just in the layers panel
Expand All @@ -546,8 +550,7 @@ npm run test
- [ ] Add Blocks. Reusable component blocks that can be used in multiple pages
- [ ] Move component schemas to separate shadcn registry to keep main registry light
- [ ] Move prop form field components (overrides) to separate shadcn registry to keep main registry light
- [ ] Add data sources (functions) to component layers (ex, getUser() binds prop user.name) - Connect variables to live data sources
- [ ] Add visual data model editor + code gen for backend code for CRUD operations
- [ ] Add visual data model editor + code gen for backend code for CRUD operations. (ex Zenstack schema editor)
- [ ] Add event handlers to component layers (onClick, onSubmit, etc)
- [ ] Update to new AutoForm when stable
- [ ] Update to Zod v4 (when stable) for native json schema conversion to enforce safety in layer props
Expand Down
2 changes: 1 addition & 1 deletion __tests__/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jest.mock("../components/ui/ui-builder/codeblock", () => {
};
});

it("App Router: Works with Server Components", async () => {
it("Main Component", async () => {
render(<Page />);
const mainPage = await screen.findByTestId("main-page");
expect(mainPage).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export const BreakpointClassNameControl = ({
>
<AccordionItem
value="classes"
className="border-t border-b-0 px-4"
className="border-t border-b-0 px-4 [&_#accordion-content[data-state=open]]:overflow-visible [&_#accordion-content[data-state=closed]]:overflow-hidden"
data-testid="classes-accordion-item"
>
<AccordionTrigger
Expand All @@ -192,7 +192,10 @@ export const BreakpointClassNameControl = ({
>
Edit Classes
</AccordionTrigger>
<AccordionContent data-testid="classes-accordion-content">
<AccordionContent
data-testid="classes-accordion-content"
id="accordion-content"
>
<ClassNameMultiselect
value={classString}
onChange={handleMultiselectChange}
Expand Down
46 changes: 39 additions & 7 deletions components/ui/ui-builder/internal/iframe-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
import React, {
useCallback,
useLayoutEffect,
useMemo,
useRef,
useState,
} from "react";
import ReactDOM from "react-dom";
import { GripVertical } from "lucide-react";
import { DragConfig, useDrag } from "@use-gesture/react";
Expand Down Expand Up @@ -61,12 +67,12 @@ export const IframeWrapper: React.FC<IframeWrapperProps> = React.memo(
);

useLayoutEffect(() => {
if (resizable && iframeRef.current) {
setIframeSize({
width: iframeRef.current.parentElement?.offsetWidth || 600, // Fallback to 600px or any default width
});
}
}, [resizable]);
if (resizable && iframeRef.current) {
setIframeSize({
width: iframeRef.current.parentElement?.offsetWidth || 600, // Fallback to 600px or any default width
});
}
}, [resizable]);

useLayoutEffect(() => {
const iframe = iframeRef.current;
Expand All @@ -86,6 +92,32 @@ export const IframeWrapper: React.FC<IframeWrapperProps> = React.memo(

iframeDoc.body.style.backgroundColor = "transparent";

// Copy font variables and classes from parent to iframe
const parentBody = document.body;
const parentHtml = document.documentElement;
const parentComputedStyle = window.getComputedStyle(parentHtml);

// Copy all CSS custom properties (variables) from parent html element
for (let i = 0; i < parentComputedStyle.length; i++) {
const property = parentComputedStyle[i];
if (property.startsWith("--")) {
const value = parentComputedStyle.getPropertyValue(property);
iframeDoc.documentElement.style.setProperty(property, value);
iframeDoc.body.style.setProperty(property, value);
}
}

// Copy font-related classes from parent body
parentBody.classList.forEach((className) => {
if (
className.includes("font-") ||
className === "antialiased" ||
className.includes("__variable")
) {
iframeDoc.body.classList.add(className);
}
});

// Function to inject stylesheets into the iframe
const injectStylesheets = () => {
// Select all linked stylesheets in the parent document
Expand Down
Loading