Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Future of Craft.js - taking it to the next level! 🚀 #507

Open
prevwong opened this issue Apr 4, 2023 · 30 comments
Open

Future of Craft.js - taking it to the next level! 🚀 #507

prevwong opened this issue Apr 4, 2023 · 30 comments

Comments

@prevwong
Copy link
Owner

prevwong commented Apr 4, 2023

Background

Hey folks! Craft.js is about 3 years old now and in that time we've seen plenty of Craft-based page editor implementations in the wild. I'm incredibly grateful for everyone who has been using Craft, and for the feedback I have gotten all this while.

Having personally worked at some companies that used Craft has helped me to recognise the strengths and as well some of the shortcomings of the framework. One of those shortcomings that I felt was in the state management system itself.

While the current EditorState in Craft works pretty well, it falls short for more complex page editor requirements. Currently, it isn't quite possible to build complete UI components with Craft.

Let's take a look at how a typical UI component built in React looks like:

const posts = [...];

const MyComponent = (props) => {
    const [counter, setCounter] = useState(0);
    return (
        <div>
            <h1>Hello World!</h1>
            {
                props.enabled && <p>I'm enabled!</p>
            }
            {
                counter > 0 && (
                    <h2>I'm non-negative!</h2>
                )
            }
            {
                posts.map(post => <h2>{post.name}</h2>)
            }
            <button
                onClick={() => {
                    setCounter(counter + 1);
                }}>
                Increment
            </button>
        </div>
    )
}

A React component is able to do the following:-

  • Render an HTML output based on the props given
  • Supports JS expressions
  • Hold stateful values
  • Render an element conditionally based on an expression
  • Iterate through an array and render an element multiple times for each item in the array

The current Craft's EditorState is essentially the equivalent of building a single UI component without states and props; and with the ability of adding/reordering JSX templates and mutating simple prop values of those JSX templates. However, as seen in the React example above, much of what is possible with building UI components with libraries like React, isn't really replicable with a page builder built with Craft.

With that in mind, I spent the past couple of months trying to build a new state management system for Craft; one that could allow end-users of your page editors to build UI components that could be as complex as ones that developers could write in React code. Due to the complexity and the breaking changes that this may possibly introduce, this new state management system is currently being worked on as a separate project, called Reka.

Just to avoid confusion, Reka is not a replacement for Craft. It's simply intended to replace the internal state management system in Craft to enable the following benefits.

Benefits

User-designed Components

End-users of your page editor will now be able to design entire components and re-use them anywhere:

reka-demo-user-components2.mp4
[
  {
    type: 'RekaComponent',
    name: 'Counter',
    props: [
      {
        type: 'ComponentProp',
        name: 'initalValue',
        init: { type: 'Literal', value: 0 },
      },
    ],
    state: [
      {
        type: 'Val',
        name: 'counter',
        init: { type: 'Identifier', name: 'initialValue' },
      },
    ],
    template: {
      type: 'TagTemplate',
      tag: 'p',
      props: {},
      children: [
        {
          type: 'TagTemplate',
          tag: 'text',
          props: { value: { type: 'Literal', value: 'My counter: ' } },
        },
        {
          type: 'TagTemplate',
          tag: 'text',
          props: { value: { type: 'Identifier', value: 'counter' } },
        },
      ],
    },
  },
  {
    type: 'RekaComponent',
    name: 'App',
    state: [],
    template: {
      type: 'TagTemplate',
      tag: 'div',
      props: {},
      children: [{ type: 'TagTemplate', component: 'Counter', props: {} }],
    },
  },
];

// which is the equivalent of the following React code:
const Counter = ({ initialValue = 0 }) => {
  const [counter, setCounter] = useState(initialValue);
  return <p>My Counter: {counter}</p>;
};

const App = () => {
  return (
    <div>
      <Counter initalValue={10} />
    </div>
  );
};

As seen above, we can now allow end-users to build UI components with states, props, expressions and nearly almost all the fundamentals that you would expect from a UI library like React.

In case you're wondering, yes - you can even render the same element from an array (like you can with .map in React)

Realtime Collaboration

Multiplayer is often a requirement for large page editor implementations and to support this, Reka provides an additional extension that enables multiplayer capabilities via Y.js CRDT which supports common protocols including Web Sockets and WebRTC.

reka-demo-collab2-faster.mov

Extensible State

Another challenge that I personally faced with Craft when building page editors was the ability of storing additional data related to the editor; previously I would store these as part of the custom property of the ROOT node in Craft. With Reka, this is now achievable in a less awkward fashion with the use of Extensions. For example, let's say you want your end-users to be able to leave a comment on a template element; you can store these comments directly as part of the State:

reka-demo-comments2.mov
import { createExtension } from '@rekajs/core';
type CommentState = {
  comments: Array<{
    templateId: string; // Id of the Template element associated with the comment
    content: string;
  }>;
};
const CommentExtension = createExtension<CommentState>({
  key: 'comments',
  state: {
    // initial state
    comments: [],
  },
  init: (extension) => {
    // do whatever your extension may have to do here
    // ie: send some data to the backend or listen to some changes made in State
  },
});

// Usage
reka.change(() => {
  reka.getExtension(CommentExtension).state.comments.push({
    templateId: '...',
    content: 'This button tag should be larger!!',
  });
});

Additional Functionalities

With the current Craft state system, you are already able to expose React components via the resolver prop so your end-users could use them. With Reka, you can continue to expose these React components but you're also able to expose other stateful values and JS functions that your end-users can interact with:

reka-demo-externals.mp4
import * as React from 'react';
import confetti from 'canvas-confetti';

const Icon = (props) => {
    return <SomeIconLibrary icon={props.icon} />    
}

const reka = Reka.create({
   externals: {
       components: [
           t.externalComponent({
               name: "MyReactIcon",
               render: (props) => <Icon {...props} />
           })
       ],
       functions: () => ({
           doConfetti: () => {
               confetti();
           }
       }),
       states: {
           scrollTop: 0
       }
   }
});

Disadvantages

A much larger and complex data structure

The current Craft EditorState is a simple implicit tree data structure, whereas Reka is an AST. As such, a Reka AST for an equivalent EditorState is expected to be larger:

// EditorState
{
    id: "ROOT",
    data: {
      type: "div",
      props: {
          text: "Hello"
      }    
    }
}

// Reka AST
{
    id: "some-random-id",
    type: "RekaComponent",
    state: [],
    props: [],
    template: {
        type: "TagTemplate",
        tag: "div",
        props: {
            text: {
                type: "Literal",
                value: "Hello"
            }
        }
    }
}

In particular, you can see how the "props" of each element is now more verbose in the AST, since now it can be any expressions (ie: pointing to some variable, or concatenating values) and not just literals.

What's next?

Reka is a massive departure from Craft's current state system, hence I started this thread to get everyone's thoughts/concerns on it. Additionally, I've written up documentation along with examples for Reka so everyone could try it out for themselves.

Then, I would like to integrate Reka into Craft - which I will admit is easier said than done as this would be somewhat of a rewrite of Craft:-

  • Integrating Craft's events and drag-n-drop system with Reka
    • Much of the logic in existing event system to handle drag-n-drop can be reused
    • We may need to introduce a way to handle drag-n-drop rules (ie: canDrag, canDrop etc) for the user-designed components in Reka.
  • Introducing higher-level methods to interact with Reka
    • We already have these now in Craft, in the form of actions and query. Perhaps, adding these to work with Reka would make it less necessary for consumers to interact with the AST directly.
  • Adding undo/redo
  • Linked Nodes(?)
    • Currently this is not supported within Reka and we need to evaluate if we should continue to support it (or if there's a better way to implement it); considering that it is often difficult to maintain and causes confusion with developers.
  • Backwards compatibility(?)
    • Ideally, it would be great to have all page editors built with the current Craft EditorState to just work with new Craft with the integrated Reka AST.
    • This may involve providing some drop-in legacy adapter that enables the existing EditorState to be transformed into the AST.

Putting it all together

reka-demo01-output1.mov

That's it for now, please feel free to let me know your thoughts below!

If you or your organisation is using Craft.js and would like to support me for my efforts - please consider sponsoring! ❤️

@prevwong prevwong pinned this issue Apr 4, 2023
This was referenced Apr 4, 2023
@neelansh15
Copy link

This is epic. Having tinkered with CraftJS, this feels more like a successor to it, having enabled anyone to create what a developer could

@hugominas
Copy link

hugominas commented Apr 4, 2023

@prevwong you have been busy :) Reka is very promising congratulations on all the work.

We are sponsoring your work and have used CraftJS extensively. For our solution we are managing state programatically and have never used Linked Nodes feature.

I must admit that Reka opens a new layer of customization which would be a great addition for our end users. Keep up the good work, count with us for testing and support!

@akhrarovsaid
Copy link

I've been following RekaJS for some time now and I have to say that I'm very impressed. Super useful. I'm very excited for the day when the internal state management in CraftJS can be fully replaced with Reka.

I'm also interested in how we can go about introducing a styling system for Reka as well? Using something like CSSTree or PostCSS to store styles in an AST and be able to modify styles on components on the fly in a responsive manner would also be very useful. Maybe it's something that we can add to the roadmap?

Thank you for your work on CraftJS and RekaJS - I've had an absolute pleasure working with them! 👍 💯

@hananint
Copy link

hananint commented Apr 4, 2023

This is AMAZING. Thank you for the great work.
Is RekaJs already integrated with craftJs?

@prevwong
Copy link
Owner Author

prevwong commented Apr 6, 2023

Thank you all for the kind words and for the continued support, I really appreciate it! ❤️

@hananint Not integrated yet, but that's the plan! 🤞

@nicosh
Copy link

nicosh commented Apr 6, 2023

Reka looks very promising, i also agree with @akhrarovsaid about introducing a styling system for Reka, but i wonder if the more complex reka structure will impact somehow on the bundle size / tree shaking / performance of the page (but i guess that used in combination with next.js and server components we can have good performances).
Backwards compatibility imho is essential.

@joshpinto6
Copy link

Very cool!

@graham-saunders
Copy link

This is incredible! I love that you're continually iterating to improve craft.js.

@jorgegonzalez
Copy link

jorgegonzalez commented Oct 18, 2023

This looks great. My biggest concern as someone building a product based on Craft.js would be backwards compatibility. I would hate to be stuck on an old Craft version and unable to upgrade due to breaking changes that could be a large undertaking to address.

@john093e
Copy link

Hi everyone :)

Wonderful work and amazing results !! I am also very interested, is there any updates on the integration with craft.js ?

@mwaaas
Copy link

mwaaas commented Mar 24, 2024

is there any updates on the integration with craft.js ?

@Criztiandev
Copy link

Yo is there any update to craft.js ?

@SOG-web
Copy link

SOG-web commented Apr 21, 2024

This is awesome and wonderful. Though just found out about this project, one thing that made me stick to craftjs is the ability to arrange and re-arrange the editor page the way I want, this feature is one of the things that made craftjs standout to me. I hope the feature will still be maintained as we move the the future of craftjs. Thanks so much for this project, you are awesome

@mwaaas
Copy link

mwaaas commented May 6, 2024

@prevwong Has the work started for integrating with reka js, any how I can help

@Hahlh
Copy link
Contributor

Hahlh commented May 6, 2024

Craft.js usage is also growing quite a lot!

Regular downloads 2024 in comparison with 2023 have roughly doubled and it's well on it's way to overtake grapejs if current trajectories continue.

image

@prevwong
Copy link
Owner Author

prevwong commented May 7, 2024

Hey folks, sorry for not posting any updates on here recently. Took me a while to actually get a prototype going, but here's a bit of a sneak peek of Reka and Craft working together!

This demo showcases a lot of the existing features from Craft (ie: dnd, selection and UI abstractions) along with the new features from Reka (ie: realtime collaboration, variables, states, repeating elements and conditional templates)

(Also: check out that unified drag and drop between the layers and component and the canvas! 😎 )

craft-reka.mp4

@hugominas
Copy link

@prevwong Amazing work! very fluid an intuitive. I hope it inherits the costumizations from craftjs that allowed for so many different implementations by the community.

@hananint
Copy link

hananint commented May 7, 2024

@prevwong great work. Can't wait to try it. when can we ?

@Hahlh
Copy link
Contributor

Hahlh commented May 7, 2024

Hey folks, sorry for not posting any updates on here recently. Took me a while to actually get a prototype going, but here's a bit of a sneak peek of Reka and Craft working together!

This demo showcases a lot of the existing features from Craft (ie: dnd, selection and UI abstractions) along with the new features from Reka (ie: realtime collaboration, variables, states, repeating elements and conditional templates)

(Also: check out that unified drag and drop between the layers and component and the canvas! 😎 )

craft-reka.mp4

Awesome, thank you for sharing, Prev.
That looks very promising, I am excited for Craft.js's future!

@prevwong
Copy link
Owner Author

prevwong commented May 8, 2024

@hugominas Yes, for sure! The goal is to achieve a similar level of ease-of-use that Craft currently offers 🚀


@hananint Still working/experimenting on the APIs at the moment, but hope to have something to try really soon. Will post more updates here!

@oyatek
Copy link

oyatek commented May 23, 2024

Hi,

Congratulations on the amazing job. We're thinking about starting using it in our product that has more than 10,000 active users around the world. I'm sure we will need to extend it in some ways. We will be happy to contribute the code to the project and become sponsors as well.

@jorgegonzalez
Copy link

Are LinkedNodes going to be compatible with Reka? @prevwong

@prevwong
Copy link
Owner Author

prevwong commented Jun 14, 2024

Are LinkedNodes going to be compatible with Reka? @prevwong

At the moment, it's not. The plan is to come up with a better alternative to Linked Nodes because in its current form - it causes quite a bit of confusion and it's not easy to maintain in a codebase.

Reka introduces slots as a way for components to accept child elements (just like props.children in React) but we can extend it to support something similar to Vue.js - named slots; this way a component could specify named slots (ie: editable areas within a component).

@alihammad99
Copy link

Is Reka combined with Craft.js yet?
By the way, I'm trying to create responsive frame devices button, I can't get the container's nodeId without selection or click event, and I can't select it in React directly, is there a way to handle responsive frame?

@jorgegonzalez
Copy link

Are LinkedNodes going to be compatible with Reka? @prevwong

At the moment, it's not. The plan is to come up with a better alternative to Linked Nodes because in its current form - it causes quite a bit of confusion and it's not easy to maintain in a codebase.

Reka introduces slots as a way for components to accept child elements (just like props.children in React) but we can extend it to support something similar to Vue.js - named slots; this way a component could specify named slots (ie: editable areas within a component).

Sounds good; I am curious what upgrading will look like if we're using LinkedNodes extensively?

@fernando-deka
Copy link

This looks awesome!! When can we expect an update on this @prevwong ?

@MrVibe
Copy link

MrVibe commented Jul 2, 2024

Maybe its just me but Reka is really confusing. You've surpassed the levels of a Page builder by defining, variables &. logic into the application. This has made the application purely for developers / designers.

@prevwong
Copy link
Owner Author

prevwong commented Jul 3, 2024

@jorgegonzalez Based on what I have currently, it would be something like this:

const YourComponent = (props) => {
  const { connect } = useNode();
  return (
     <div ref={connect}>
        {props.slots.editableHeader}
    </div>
  )
}

YourComponent.craft = {
    slots: { 
       editableHeader: { 
          accepts: [Header]
       } 
    }
}

Basically, the definition of "linked nodes" would be made on the component schema level rather than in the template level.


@fernando-deka Don't have a fixed date yet, still juggling a couple of things. Hopefully in the next couple of months 🤞


@MrVibe Why is that confusing though? I don't think page builders are limited to being basic drag and drop HTML editors, so Reka provides functionalities that you would typically need in more advance/complex editors. Plus you don't have to use the new features that Reka provides if the editor you're designing doesn't need them, you could just use it as you use Craft currently.

@andreiwow2
Copy link

Hey guys, I am looking to integrate a page builder into our project for internal use to build websites faster from already made components, while looking at craft.js I found this topic about Reka, I didn't really understand if Reka is meant to be a standalone solution or to be used together with Craft.js?

Also should I start now with Reka or just craft.js considering I havent done any progress yet?

@alexandrosk
Copy link

@jorgegonzalez Based on what I have currently, it would be something like this:

const YourComponent = (props) => {
  const { connect } = useNode();
  return (
     <div ref={connect}>
        {props.slots.editableHeader}
    </div>
  )
}

YourComponent.craft = {
    slots: { 
       editableHeader: { 
          accepts: [Header]
       } 
    }
}

Basically, the definition of "linked nodes" would be made on the component schema level rather than in the template level.

@fernando-deka Don't have a fixed date yet, still juggling a couple of things. Hopefully in the next couple of months 🤞

@MrVibe Why is that confusing though? I don't think page builders are limited to being basic drag and drop HTML editors, so Reka provides functionalities that you would typically need in more advance/complex editors. Plus you don't have to use the new features that Reka provides if the editor you're designing doesn't need them, you could just use it as you use Craft currently.

Just get some VC funding my man and good luck! Craft is a great product and has huge potential!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests