MUI Hidden Component and Server Side Rendering

Hi there! I am on a project that has been using JSONForms with React and MUI for quite some time now to great effect. Thanks for all of your work on this so far!

I am currently encountering an issue as I attempt to implement a new piece of functionality for our users. I am hoping to render a string of the HTML out of our previously completed forms in read only mode for use elsewhere within our backend. I have the rest of the stuff around this figured out, and was hoping to leverage the React DOM renderToString function to do this, sort of analogous to a server side rendering/isomorphic javascript approach. The issue that we’re currently stuck on is that I cannot get the whole form to render out within this context. We are using React and MUI, and upon finding this thread, it seems that between the use of the use of the deprecated hidden component and that component now being inaccessible from the MUI theme (so it isnt possible switch it to using the css implementation via the prop), that doing a server side rendering setup like this is not currently possible.

I did find the 3.3 milestone on github which shows that there is a plan to remove this at some point in the future. My question is, if my understanding of this situation is correct, is there any sort of available workaround in the meantime? My thoughts so far are:

  • Implement my own version of the MaterialLayoutRenderer component: this is where that <Hidden> lives. I could copy over the component to use as a base, implement the change for the time being, and that should get me off to the races. This seems a bit goofy, but this would keep us on track with our deliverable dates, with the only overhead being removing this once 3.3 comes out.
  • We had started with the material renderers as a base, but at this point are using custom renderers for everything. Could I possibly switch to the React Vanilla renderers package? My renderers themselves contain a bunch of MUI elements, but that should be encapsulated enough that I can figure out any discrepancies from there. As long as it wont break the form overall, I should be safe to make that change.
  • Alternatively, is there a way hand over a schema and data to JSONforms core and render out a form with its data appropriately, just in read only mode? If I can isolate things into this use case I can get away with using a stripped down version of the form with no real MUI styling for this use case, so I can lose the specific UI appearance. Its also just in read only mode, so I don’t need any amount of interactivity. I just need to make sure that I get the same schema rules (like SHOW and HIDE, along with some if...then uses) applied and the questions and the data appropriately correlated. Ultimately I just need to correctly represent the contents of a previously completed form for use in our backend, so if this is possible, it may even be simpler.

Please let me know if that makes sense, or if any other clarification is needed. Thank you!

Hi @franklinLogan,

The issue with Hidden is, that it’s not rendering anything if the media queries are not available. In our tests we solved this with some mocks, you could try to use them in your SSR environment as well.

If that is the only use of Hidden left in your code base, then absolutely go for it. Just copy the existing renderer and replace the Hidden with a return null.

You could try to go this route, however the React Vanilla renderers definitely behave differently from the React Material ones. So you need to double check whether that impacts you. If it’s really only the MaterialLayoutRenderer which needs replacement, then I would just reimplement that one.

There is no built-in way for this. However this could be achieved with a small amount of custom renderers, covering all the UI Elements which you are currently using, i.e. one for controls, and 1-2 for layouts.

I’m wondering why you need JSON Forms rendering to achieve this. Isn’t the data you store already representing the form state? Even if you store “additional” information like data of fields which are hidden, somewhere you must already be able to extract the “real” data? Or is that solely encoded in the UI Schema rules?

Generally I would avoid tying UI logic into your data flows. I would like to recommend setting up a process in which, once the user is done manipulating the form, you are able to extract the relevant data without invoking a UI rendering framework like JSON Forms again.

I ended up starting with this approach and so far it has worked pretty well! I have a bunch more forms to convert to be sure that there is no issue but this seems promising enough to be able to figure the rest out on my own.

Ah, I definitely was a bit vague in my intended use case, sorry about that. I will try to give a bit more detail; NDAs will make it still a bit vague but I think we can make it work:

We currently have about 8 - 10 forms in our app that we are using JSONForms to render. This number will increase at least every six months, if not more often. We have thousands of completed responses for each of these forms stored in our database. What we’re hoping to do is to take the contents of these filled out forms and get them into a PDF. These PDFs would subsequently be used in a legal context, so the consequence for any potential misrepresentation of the data could be severe. These PDFs also contain sensitive personal data, so there are some security concerns with generating them all purely client side.

We don’t contain every bit of logic in the the UI schemas, but certain things are. For example: user permissions will allow one user to see a question and its response but not others. We already prevent that data from coming back in the back end , but also need to hide the question in the UI as well. Also, our custom renders take data that is stored for a response and formats it so that it is more human readable. There are other edge cases, and like I mentioned earlier, these will only increase as we increase the amount of forms, and we will need to be able to export from all of these forms in perpetuity.

We could write an interpreter that would take a form schema and the form data and appropriately render out the form, but my concern with that is maintaining a parallel structure like that feels brittle and opens us up to regression in a number of ways.

What I am attempting to explore right now is if there is any way we could leverage our currently existing JSONForms based architecture to render the form, both the questions and the answers, out so that we can minimize any opportunity of regression. The real way to do this is probably to take a server side rendering approach so that I can get the form contents without any of that data making it to the client. The solution of replacing the MaterialLayoutRenderer now makes that possible for us, so thank you for your help on that! This would require a change to some adjacent architecture within our app since we aren’t doing SSR yet, but at least that is a tractable problem. I am just trying to explore all of the other possible options for now, even if they are less than ideal, just in case our timelines do not allow for the work to alter our current architecture.

Could I trouble you for just a bit of guidance on this? If you could give me a rough sense of how to approach it, I could figure out the rest from there.

Thank you so much for taking the time to respond! I know this is a lengthy topic, so we really appreciate your input.

Hi @franklinLogan,

That’s an interesting use case. If it’s all about legal constraints you might even want to store screenshots in the PDF instead of the HTML/JS.

For example you could use a headless Browser on the server, use your page in the same way your users do and use print-to-pdf to replicate the form to PDF.

It’s basically writing a renderer set from scratch just very generic and minimal.

For example the following renderer renders arbitrary properties (i.e. string, number etc.):

const ReadOnlyControl = (props: ControlProps) => {
  if (!props.isVisible) return null; // takes care of visibility rules
  return (<div>
    <div> {{props.label}} </div>
    <div> {{props.data}} </div>
 </div>);
}

const ReadOnlyControlRenderer = withJsonFormsControlProps(ReadOnlyControl);
const tester = rankWith(1, uiTypeIs('Control'));

const readOnlyControlEntry = {
  renderer: ReadOnlyControlRenderer,
  tester: readOnlyControlTester
}

Now do the same with a layout renderer taking care of HorizontalLayout, VerticalLayout and Group . Then basic forms are already covered. Depending on your use cases you then also need to add

  • A control renderer for objects
  • A control renderer for arrays
  • Renderers for the remaining UI Schema types, e.g. Category and Label

If such a simple renderer set is sufficient then this should be doable within an afternoon.

Thats actually another one of the approaches we have been considering! Definitely has some amount of a overhead but not as much as SSR for sure. I have to have actual text instead of just images but I actually have a proof of concept of this much working so I am not too worried about that part.

This could definitely be tenable for us. Our proof of concept adds an export mode by setting a flag in the config prop, and off of that flag dramatically simplifies the output, so it might be possible. What from the core package would I then call to render out the actual form itself from the schemas? I am having a hard time pinpointing this from the api reference, sorry.

Our app is a monorepo, and with the information you have given me here I have managed to move some of our custom renderers into a shared workspace between our front end and back end, and am using some of the react-dom SSR related functionality to successfully render out html from an isolated react component with a completed form as a proof of concept, so I may already have all that I need now. I am just stacking up back up plans at this point.

Once again, thank you so much for the assistance thus far! It has been a huge help.

Hi @franklinLogan,

I think there is a misunderstanding. The example I gave was still tailored to React and requires the usual React SSR approach for rendering. It was just an alternative in case the Material UI renderers were too unwieldy for you.

There is no “VanillaJS” renderer set for JSON Forms, i.e. there is no core function to produce a form out of JSON Schema and UI Schema. The JSON Forms core is rather just a set of utilities with which it is easy to build a JSON Schema based form rendering component in the framework of choice. Of course something like a VanillaJS processor could be implemented, however it will be a magnitude more effort than just implementing a few React based renderers against the existing bindings.

Ah okay, I understand now. This is the approach we’re currently working towards so it’s good to know we’re going down a path that makes sense. So far this is going relatively well, if I have anything that might be of use to the community or if I have any sort of issues I will be sure to reach out. Thanks for the help!