This page demonstrates Shinmun’s powerful React integration, showcasing how to embed interactive React components directly in your Markdown pages using TypeScript.
React is a JavaScript library for building user interfaces. Combined with TypeScript, it provides:
Shinmun automatically loads React from a CDN via import maps, so you can start building components immediately.
Let’s start with a simple React component that renders a greeting. This demonstrates:
React.CSSPropertiesSource Code:
interface HelloProps {
name: string;
}
function Hello({ name }: HelloProps) {
return (
<div style={{ padding: '1rem', background: 'linear-gradient(...)' }}>
<h2>Hello, {name}! 👋</h2>
<p>This is a React component embedded in Shinmun.</p>
</div>
);
}
A more advanced component demonstrating multiple hooks and CSS-in-JS patterns:
useState for current themeuseEffect for transition animationsRecord<K, V> for theme configurationsTypeScript Patterns Used:
// Union type for theme options
type Theme = 'light' | 'dark' | 'ocean' | 'forest';
// Record type for theme configurations
const themes: Record<Theme, ThemeConfig> = { ... };
// useEffect for side effects
useEffect(() => {
setIsTransitioning(true);
const timer = setTimeout(() => setIsTransitioning(false), 300);
return () => clearTimeout(timer); // Cleanup!
}, [currentTheme]);
Interactive bar chart showcasing data visualization with React:
useEffect intervalsAnimation Pattern:
// Smooth CSS transitions
const barStyle: React.CSSProperties = {
height: `${(point.value / maxValue) * 160}px`,
transition: 'height 0.5s cubic-bezier(0.4, 0, 0.2, 1)',
transform: isAnimating ? 'scaleY(0.1)' : 'scaleY(1)',
transformOrigin: 'bottom'
};
A photo gallery component perfect for blogs with image content:
Key Patterns:
// Category filtering with useState
const [category, setCategory] = useState<Category>('all');
const filteredPhotos = category === 'all'
? photos
: photos.filter(p => p.category === category);
// Modal state management
const [selectedPhoto, setSelectedPhoto] = useState<Photo | null>(null);
A reading progress component commonly used in blog posts:
Scroll Tracking Pattern:
// useCallback for optimized scroll handler
const handleScroll = useCallback(() => {
const { scrollTop, scrollHeight, clientHeight } = contentRef.current;
const scrollProgress = (scrollTop / (scrollHeight - clientHeight)) * 100;
setProgress(Math.min(100, Math.max(0, scrollProgress)));
}, []);
// useEffect for scroll event listener
useEffect(() => {
content.addEventListener('scroll', handleScroll);
return () => content.removeEventListener('scroll', handleScroll);
}, [handleScroll]);
Create your component in public/apps/:
// public/apps/my-widget.tsx
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
function MyWidget() {
const [value, setValue] = useState('');
return (
<div style={{ padding: '1rem', background: '#f5f5f5' }}>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Type something..."
/>
<p>You typed: {value}</p>
</div>
);
}
// Mount to container
const container = document.getElementById('my-widget');
if (container) {
const root = createRoot(container);
root.render(<MyWidget />);
}
@@typescript-file[my-widget](public/apps/my-widget.tsx)
Shinmun handles:
// Basic
const [count, setCount] = useState(0);
// With type
const [user, setUser] = useState<User | null>(null);
// Lazy initialization
const [data, setData] = useState(() => expensiveInit());
// On mount/unmount
useEffect(() => {
console.log('mounted');
return () => console.log('unmounted');
}, []);
// On dependency change
useEffect(() => {
fetchData(id);
}, [id]);
const computed = useMemo(() => {
return expensiveCalculation(input);
}, [input]);
const handler = useCallback((e: Event) => {
handleEvent(e, dependency);
}, [dependency]);
public/
apps/
hello-react.tsx # Simple greeting
counter-react.tsx # Counter with hooks
todo-react.tsx # Full CRUD todo list
theme-switcher.tsx # Theme switching demo
animated-chart.tsx # Data visualization
photo-gallery.tsx # Photo gallery with lightbox
reading-progress.tsx # Blog reading progress indicator
form-validation.tsx # TypeScript form validation
search-filter.tsx # Real-time search
npm install esbuildpublic/apps/@@typescript-fileReact and ReactDOM are automatically loaded from a CDN—no additional dependencies needed!