Suggestions for making ReDoc ready for Next.js SSR
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 25.9k
- Forks
- 2.4k
- Avg merge
- 13h 10m
- Merged PRs (30d)
- 4
Description
Hi,
I recently enabled ReDoc to run with static props and fully server-side rendered in Next.js and I would like to take the opportunity to report the gist of my journey.
I am hopeful that this will help others facing the same challenge and I would be especially happy to discuss a few things with @RomanHotsiy , what I had to change and if you would consider it as feasible to generalize what I did and make a PR.
Next.js page boilerplate
My page file looks roughly like this. The inline comments should give an idea on some of the tricky parts I had to find a solution for to avoid mismatches between the server-side generated code and the hydrated client-side.
function Docs({state}) {
// I had issues with serializing dates so I had to use custom JSON replacer/reviver to patch those
const reviver = (key, value) => {
if (typeof value === "string") {
try {
const date = new Date(value);
if (date instanceof Date && !isNaN(date)) {
return date;
}
} catch {}
}
return value;
};
const store = AppStore.fromJS(JSON.parse(state, reviver), data);
return (
<div>
<Redoc store={store} />
</div>
);
}
export default Docs;
export async function getStaticProps(context) {
// Reset tab id counter to make sure that client/server code match for react-tabs
resetIdCounter();
// here the specs are loaded from a local spec file, alternatively use a public URL and set the specUrl positional param.
const specFile = path.join(/*<path to a locally stored file>*/)
const spec = await loadAndBundleSpec(specFile);
// The search index produced mismatch errors between client and server side.
// Not sure if this could be fixed by investigating deeper into the underlying library, but I disabled it here for peace of mind.
// Native scrollbars were disabled for similar reasons
const options = {
disableSearch: true,
nativeScrollbars: true,
};
const store = new AppStore(spec, null, options);
// Serialize the store to be used for the client hydration
const state = await store.toJS();
// I had issues with serializing dates so I had to use custom JSON replacer/reviver to patch those
const replacer = (key, value) => {
if (typeof value === "Date") {
return value.toJSON();
}
return value;
};
return {
props: {
state: JSON.stringify(state, replacer),
},
};
}
Changes to Redoc codebase
Here is a (hopefully complete) list of things I had to change in my Redoc fork:
specUrl should be "nullable"
In the current typescript implementation specUrl is of type string | undefined. In my particular case (*obviously not if you'd use the public url variant by setting specUrl), this was causing trouble, because undefined can not be serialized properly. It was relatively easy to fix, by replacing the type of specUrl everywhere with string | undefined | null.
I also had to make a change in src/utils/openapi.ts and replace the test specUrl === undefined with !specUrl
@RomanHotsiy would you consider this as feasible to be changed on the upstream repo? If yes, I would create a PR for that.
IS_BROWSER-dependent base url determination
There are several places in the app, where IS_BROWSER is used to determine leaves of the state, particularly for determining the url base from the window.location.
Two files that I had to touch
src/utils/openapi.ts
src/services/OpenAPIParser.ts
I removed the switches and just set the default to an empty string. I know that sacrificing the base url was not the most elegant solution, but it worked for now, before I decided on a more general solution.
@RomanHotsiy What would you say, about making this an option, so users could have full control over the base and in my case avoid mismatches between server and client HTML? I could create a PR if you like.
perfect-scrollbar Global CSS
In src/common-elements/perfect-scrollbar.tsx I had to remove the following line and the corresponding reference to the global style injection, because Next.js does not allow Global CSS in it's child components
import psStyles from 'perfect-scrollbar/css/perfect-scrollbar.css';
Reference documentation: https://github.com/vercel/next.js/blob/master/errors/css-global.md
This point has maybe the biggest potential for controversial discussion as this is a very specific requirement for Next.js, but finding a general solution to this would make me very happy, so I don't have to disable the perfect-scrollbar functionality (I probably will not really need it for my company's documentation).
Search index
This is a topic I didnt investigate yet, but if there is any suggestion how to solve this, I am happy to get feedback.
(optional) Date object de-/serialization
As mentioned as code comment in the above boilerplate, I needed to use a replacer/reviver for making the app's state serializable. I was sensing a rabbit hole to fix this in the underlying code base, so I just added it in my integrating code, but of course, it would be super awesome to be fixed.
PS.: @RomanHotsiy To convince my team to go with Redoc instead of Slate, I also extended the code base to integrate Markdown files via MDX loader in order to support the Slate-style markdown. This is still WIP, but of course, I would also be interested in creating a PR, although I would like to get an idea from the maintainers first, if this would be appreciated, given that you recently created a developer-portal repo on your own and there might be conflict of interests with your SaaS offerings.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the SSR-related changes described for src/utils/openapi.ts, src/services/OpenAPIParser.ts, and src/common-elements/perfect-scrollbar.tsx. Compare the requested specUrl, base URL, CSS, search-index, and state-serialization changes with the existing implementation and determine which proposal has an agreed scope. Done means a maintainer-approved plan or a clearly bounded change with matching validation identified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- next.js, react, typescript
- Domain
- frontend, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100