Skip to content

Conversation

@brunoamui
Copy link
Collaborator

What is the purpose of this pull request?

This PR migrates the components library to use Next.js's next/image component for automatic image optimization and fixes critical dependency bundling issues that were preventing proper integration with consuming applications (helpcenter).

What problem is this solving?

Problem 1: No image optimization

  • Images were rendered as plain <img> tags without optimization
  • No automatic resizing, format conversion, or lazy loading
  • Larger bundle sizes and slower page loads
  • Missing blur placeholders and modern image formats (WebP, AVIF)

Problem 2: Bundling Next.js and React in components library

  • next and react-dom were in dependencies instead of devDependencies
  • Caused components to bundle their own separate Next.js instance
  • Consuming apps couldn't configure Next.js image domains (hostname errors)
  • Created duplicate React instances leading to hydration errors

Problem 3: ESM/CommonJS interop issues

  • next/image imports from ESM modules returned objects instead of components
  • Inconsistent import paths (next/image vs next/image.js)
  • Runtime errors: "React.jsx: type is invalid ... got: object"

Problem 4: SSR errors with useRouter

  • Components using next/router failed in SSR contexts
  • "NextRouter was not mounted" errors
  • Incompatibility between pages router and app router contexts

What changed?

1. Migrated to Next.js Image component 🖼️

// Before: Plain img tag
<img src={props.src} alt={props.alt} />

// After: Optimized Next.js Image
<Image
  src={props.src}
  alt=""
  width={imgWidth}
  height={imgHeight}
  placeholder="blur"
  blurDataURL={data.base64}
  sizes="100vw"
/>

2. Fixed dependency structure 📦

// Moved from dependencies to devDependencies
"next": "13.2.4"
"react-dom": "18.2.0"

// Removed from peerDependencies (consumer controls these)
- "next": "13.2.4"
- "react-dom": "18.2.0"

3. Added CommonJS default export fallback 🔧

import NextImage from 'next/image.js'
const Image = (NextImage as any).default || NextImage

4. Fixed router imports for SSR compatibility 🔀

// Before: Direct import
import { useRouter } from 'next/router'

// After: Compatibility router
import { useRouter } from 'next/compat/router.js'

5. Updated bundler config ⚙️

// tsup.config.ts
external: [
  'react',
  'react-dom',
  /^next\//  // Externalize all next/* modules
]

How should this be manually tested?

In the components library:

# 1. Install dependencies
yarn install

# 2. Build the library
yarn build

# 3. Verify dist contains proper Next.js imports
grep -r "next/image" dist/

In consuming app (helpcenter):

# 1. Point to this branch in package.json
"@vtexdocs/components": "github:vtexdocs/components#feat/nextjs-image-optimization"

# 2. Reinstall
rm -rf node_modules/@vtexdocs/components && yarn install

# 3. Clear Next.js cache
rm -rf .next

# 4. Start dev server
yarn dev

# 5. Navigate to page with images (e.g., /docs/tutorials/...)

# 6. Verify in DevTools Network tab:
# - Images served via /_next/image?url=...
# - Optimized formats (WebP/AVIF)
# - Proper lazy loading
# - No hostname configuration errors

Benefits

Image optimization:

  • ✅ Automatic format conversion (WebP, AVIF)
  • ✅ Responsive images with proper srcset
  • ✅ Lazy loading out of the box
  • ✅ Blur placeholders during load
  • ✅ Smaller image sizes and faster loads

Dependency management:

  • ✅ No duplicate Next.js instances
  • ✅ Consuming app controls Next.js version and configuration
  • ✅ Proper module resolution
  • ✅ Smaller bundle size (no bundled Next.js)

Compatibility:

  • ✅ Works with SSR and CSR
  • ✅ Compatible with pages and app router
  • ✅ ESM and CommonJS interop
  • ✅ No hydration errors

Files changed

Source files:

  • src/lib/markdown-renderer/components.tsx - Migrated to Next.js Image
  • src/components/whats-next-card/index.tsx - Migrated to Next.js Image
  • src/components/sidebar-elements/index.tsx - Fixed router import
  • src/components/search-*.tsx - Fixed router imports
  • src/utils/sidebar-utils.ts - Added SSR window checks

Configuration:

  • package.json - Moved dependencies, updated peerDependencies
  • tsup.config.ts - Externalized Next.js modules

Build artifacts:

  • dist/* - Rebuilt with new imports and externals

Breaking changes

None - This is backward compatible:

  • Components still export the same API
  • Image component props remain the same
  • Consuming apps just need Next.js image domain configuration

Types of changes

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Requires change to documentation, which has been updated accordingly.

Related PRs

Consuming app (helpcenter):

Commits in this PR

  1. feat: migrate ImageComponent to Next.js Image for optimization
  2. fix: mark Next.js modules as external in bundler config
  3. fix: handle SSR in updateOpenPage by checking window availability
  4. fix: use next/compat/router to avoid SSR errors with useRouter
  5. fix: add .js extension to next/compat/router imports for ESM
  6. fix: change next/image.js imports to next/image for proper ESM resolution
  7. fix: add CommonJS default export fallback for next/image imports
  8. fix: move next and react-dom from dependencies to devDependencies

Testing checklist

  • Components build successfully (yarn build)
  • No TypeScript errors (yarn type-check)
  • Images render correctly in helpcenter with this branch
  • Images use Next.js optimization (check Network tab for /_next/image)
  • No console errors about unconfigured hostnames
  • No hydration errors
  • SSR works correctly
  • No duplicate React/Next.js warnings

- Replace plain <img> tags with Next.js Image component
- Add automatic format optimization (WebP/AVIF)
- Implement lazy loading for better performance
- Preserve blurhash placeholder functionality from plaiceholder
- Maintain LightBox modal integration
- Update styles to support Next.js Image wrapper structure
- Remove ESLint warning for next/next/no-img-element

Features:
- Automatic image optimization and format conversion
- Lazy loading reduces initial page load time
- Responsive images with srcset
- Blurhash placeholders for smooth UX
- LightBox functionality fully preserved

BREAKING CHANGE: Requires Next.js 13.2.4+ as peer dependency
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request does not contain a valid label. Please add one of the following labels: ['release-no', 'release-auto', 'release-patch', 'release-minor', 'release-major']

@carolinamenezes carolinamenezes added the release-patch Patch version bump label Oct 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-patch Patch version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants