Thank you so much Sarah, this was incredibly helpful! Like many others, we had poured through the documentation and it wasn’t clear that this was possible using codegen (even though we knew it HAD to be). As you confirmed, ISR is ABSOLUTELY POSSIBLE WITH CODEGEN!
Your proposed solution elegantly maintains all the benefits of Plasmic’s visual data integrations while giving us the performance we need. This helped us avoid a more complex code component approach (which we will tackle later).
Since we successfully implemented ISR with Plasmic codegen we wanted to share our solution for others facing the same client side loading delays / issues.
The Solution: ISR with Pre-fetched Plasmic Queries
We created a centralized ISR system that pre-fetches Plasmic data at build time. Here’s the high-level approach:
- Extract Plasmic queries during build using
extractPlasmicQueryData
from @plasmicapp/react-web/lib/prepass
- Wrap data in PlasmicQueryDataProvider to hydrate components with pre-fetched data
- Centralized ISR helpers to make implementation simple across pages
Key Implementation Points:
- Performance: 10+ second loads → ~3ms (99% improvement!)
- Simple adoption: Just 3 lines to add ISR to any page
- Production-ready: 15-minute revalidation with automatic background updates
- No client-side fetching: All data included in initial HTML
Quick Example:
// Before: 10-second client-side loading
export default function MyPage() {
return <PlasmicMyPage />; // Shows loading states
}
// After: Instant server-rendered content
import { IsrPageWrapper, createIsrStaticProps } from '../lib/isr';
export default function MyPage(props) {
return (
<IsrPageWrapper {...props}>
<PlasmicMyPage />
</IsrPageWrapper>
);
}
export const getStaticProps = createIsrStaticProps('/my-page', PlasmicMyPage);
Hybrid _app.tsx for ISR Support
We modified _app.tsx
to create a “fast path” for ISR pages that bypasses heavy client-side initialization during server-side rendering:
// Detect ISR pages using explicit markers
const isIsrPage = Boolean(
pageProps._isrEnabled ||
Component.isrEnabled ||
pageProps.queryCache
);
// Fast path: minimal server-side rendering for ISR pages
if (typeof window === 'undefined' && isIsrPage) {
return (
<PlasmicRootProvider>
<Component {...pageProps} />
</PlasmicRootProvider>
);
}
// Regular path: full initialization for client-side and non-ISR pages
// ... existing app logic ...
This hybrid approach ensures ISR pages render quickly during build time without unnecessary session checks or client-side state initialization, while preserving all functionality for interactive pages. The dual-path architecture was crucial for achieving the sub-10ms server render times.
Bonus: Debug Your ISR Pages
Adding a simple prop to ISR pages helps with debugging and monitoring:
// In your page component
function FocusPage(props: FocusProps) {
// Access ISR build info via props (only in dev/preview)
if (process.env.NODE_ENV !== 'production' && props.buildId) {
console.log('ISR Debug:', {
buildId: props.buildId,
buildTime: props.buildTimestamp,
queriesFound: props.queriesFound,
hasCache: !!props.queryCache
});
}
return (
<IsrPageWrapper {...props}>
<PlasmicFocusAreas />
</IsrPageWrapper>
);
}
The ISR system automatically provides these props during build/regeneration:
buildId
- Unique identifier for each build
buildTimestamp
- When the page was generated
queriesFound
- Number of Plasmic queries pre-fetched
queryCache
- The actual cached data
This helps verify ISR is working correctly without affecting production performance!
Gotchas We Solved:
- SSR-safe expressions: Updated Plasmic expressions like
window.getPlasmicSession()
to include typeof window !== 'undefined'
checks
- Build warnings: Fixed “window is not defined” errors in components
- Environment detection: Different revalidation times for dev vs production
- Fixed “Loading…” displayed during/after hydration: Removed React Suspense boundaries for ISR pages by conditionally rendering children directly when
hasIsrData
is true, preventing the fallback from appearing during hydration even though all data was already pre-fetched in the HTML
- Fixed content flash/duplication during hydration: Resolved hydration mismatch where server rendered ISR content immediately but client initially returned null - solution was to remove
typeof window === 'undefined'
condition from ISR detection so both server and client use identical render paths when isIsrPage
is true, eliminating visual artifacts while preserving all ISR functionality
The system works perfectly with Plasmic’s PostgreSQL integration - all opId
queries are pre-fetched and cached. Happy to share more details if helpful for documentation!
Thanks again for your help!
Keywords for search: plasmic SSR ISR SSG codegen getStaticProps getServerSideProps data.plasmic.app extractPlasmicQueryData server side rendering static generation data fetching nextjs vercel build time pre-fetch cache performance PlasmicQueryDataProvider react-web prepass revalidate hydration incremental regeneration