- React
React 19の新機能まとめ|19.3までの一覧とCompilerを実測
React 19の新機能とは、フォーム送信などの非同期処理を「Actions」としてまとめて扱える仕組みと、それを支える新しいフック・API群のことです。2024年12月の 19.0 以降も、19.1(2025年3月)、19.2(2025年10月)、19.3(2026年9月9日)と機能が追加され、自動でメモ化する React Compiler 1.0(2025年10月)も安定版になりました。
この記事では、react.dev のブログと公式リリースノートで確認できた新機能をバージョン別の一覧表にまとめ、Vite 8.3.1 + React 19.3.0 で動くサンプルを作って Playwright で操作した結果を載せています。React Compiler を入れたときにビルド出力がどう変わり、再計算が何回減ったか、React 18 から移行するときに実際に出た型エラーも掲載しています。
React 19の新機能の早見表(19.0〜19.3)
| 版 | 機能 | ひとことで | 分類 |
|---|---|---|---|
| 19.0 | Actions(非同期の startTransition) |
送信中・エラー・完了をまとめて扱う | 状態管理 |
| 19.0 | useActionState |
Action の結果と送信中フラグを返すフック | フック |
| 19.0 | useOptimistic |
結果を待たずに仮の表示を出す | フック |
| 19.0 | useFormStatus(react-dom) |
親の <form> の送信状態を読む |
フック |
| 19.0 | <form action={関数}> |
フォームに関数を渡せる。成功後に自動リセット | DOM |
| 19.0 | use() |
Promise や Context を描画中に読む | API |
| 19.0 | ref as prop | 関数コンポーネントが ref を props で受け取れる(forwardRef 不要) |
改善 |
| 19.0 | <Context> as provider |
<Context.Provider> の代わりに <Context> |
改善 |
| 19.0 | ref のクリーンアップ関数 | ref コールバックから片付け関数を返せる | 改善 |
| 19.0 | useDeferredValue の初期値 |
第2引数で初期値を指定 | 改善 |
| 19.0 | ドキュメントメタデータ | <title> <meta> <link> を部品内に書くと <head> へ |
DOM |
| 19.0 | スタイルシートの precedence |
読み込み順と重複を React が管理 | DOM |
| 19.0 | async script / preload / preinit など |
外部リソースの先読み API | DOM |
| 19.0 | onCaughtError / onUncaughtError |
エラー報告をルートで一括設定 | エラー |
| 19.0 | Custom Elements 完全対応 | Web Components の属性・プロパティを正しく扱う | DOM |
| 19.0 | Server Components / Server Actions | サーバーで描画・実行する部品と関数 | RSC |
| 19.1 | Owner Stack(captureOwnerStack) |
どの部品が描画させたかを開発時に取得 | デバッグ |
| 19.1 | Suspense の改善 | 水和(ハイドレーション)の優先度などを改善 | 改善 |
| 19.2 | <Activity> |
画面を状態を保ったまま隠す・先に描画しておく | 部品 |
| 19.2 | useEffectEvent |
Effect の中で最新の値を読むイベント関数 | フック |
| 19.2 | cacheSignal |
RSC の cache() の寿命が尽きたら中断 |
RSC |
| 19.2 | Performance Tracks | Chrome DevTools に React の処理を表示 | デバッグ |
| 19.2 | 部分的な事前レンダリング(resume など) |
静的部分を先に作り、後で続きを描画 | SSR |
| 19.2 | useId の接頭辞が _r_ に |
View Transitions 等との互換のため | 変更 |
| 19.3 | <ViewTransition>(安定版) |
要素の出入り・移動をアニメーション | 部品 |
| 19.3 | addTransitionType |
遷移の理由ごとにアニメーションを変える | API |
| 19.3 | Fragment refs(安定版) | <Fragment ref> で複数のDOMをまとめて操作 |
改善 |
| 19.3 | browser()(react-dom) |
use(browser()) でその部品をSSRから外す |
DOM |
| 19.3 | Trusted Types 対応 | CSP の Trusted Types を文字列化せずに渡す | セキュリティ |
| 19.3 | Server Components で <Context> を直接描画 |
Provider 用のラッパー部品が不要に | RSC |
| 別枠 | React Compiler 1.0(2025年10月7日) | useMemo / memo を自動で入れるビルド時ツール |
ツール |
まず押さえるのは「Actions 系の4つ(useActionState・useOptimistic・useFormStatus・form action)」と「use()」「ref as prop」、そして React Compiler です。19.1 は react.dev のブログ記事がないため、GitHub の公式リリースノート(v19.1.0)で確認しました。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「React 19の新機能まとめ|19.3までの一覧とCompilerを実測」
動作確認の環境
react / react-dom 19.3.0(npm view react version で確認)、@types/react 19.3.0、vite 8.3.1、@vitejs/plugin-react 6.1.1、babel-plugin-react-compiler 1.0.0、@rolldown/plugin-babel 0.2.4、@babel/core 8.0.6、TypeScript 5.9.3、Node.js 22.22.2、Playwright 1.56.1(Chromium 141)で確認しました。
動く最小サンプル(Vite + React 19.3)
新機能をひととおり使った小さなアプリです。このまま貼れば npx vite build と npx vite preview で動きます。
セットアップ
npm init -y
npm i react@19.3.0 react-dom@19.3.0
npm i -D vite@8.3.1 @vitejs/plugin-react@6.1.1 typescript@5 @types/react @types/react-dom @types/node
# React Compiler(後半で解説)
npm i -D babel-plugin-react-compiler@1.0.0 @rolldown/plugin-babel @babel/core
index.html は <div id="root"></div> と <script type="module" src="/src/main.tsx"></script> を置くだけです。
// src/main.tsx
import { createRoot } from 'react-dom/client'
import { App } from './App'
createRoot(document.getElementById('root')!).render(<App />)
// src/api.ts(サーバーの代わり。待ち時間を入れた疑似API)
export type Task = { id: number; title: string }
const tasks: Task[] = [{ id: 1, title: '備品の棚卸し' }]
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms))
export async function fetchTasks(): Promise<Task[]> {
await wait(500)
return [...tasks]
}
export async function addTask(title: string): Promise<Task> {
await wait(800)
const task = { id: tasks.length + 1, title }
tasks.push(task)
return task
}
Actions:useActionState・useOptimistic・useFormStatus
// src/TaskForm.tsx
import { useActionState, useOptimistic } from 'react'
import { useFormStatus } from 'react-dom'
import { addTask, type Task } from './api'
type State = { tasks: Task[]; error: string | null }
// useFormStatus:親の <form> の送信状態を読む(props のバケツリレー不要)
function SubmitButton() {
const { pending } = useFormStatus()
return <button disabled={pending}>{pending ? '送信中…' : '追加'}</button>
}
export function TaskForm({ initialTasks }: { initialTasks: Task[] }) {
// useActionState:非同期の処理(Action)の結果を state として持つ
const [state, formAction] = useActionState(
async (prev: State, formData: FormData): Promise<State> => {
const title = String(formData.get('title') ?? '').trim()
if (title.length < 2) return { ...prev, error: '2文字以上で入力してください' }
addOptimistic({ id: -1, title }) // 楽観的に先に表示
const task = await addTask(title)
return { tasks: [...prev.tasks, task], error: null }
},
{ tasks: initialTasks, error: null },
)
// useOptimistic:サーバーの結果を待たずに仮の表示を出す
const [optimisticTasks, addOptimistic] = useOptimistic(
state.tasks,
(current: Task[], newTask: Task) => [...current, newTask],
)
return (
<form action={formAction}>
<input name="title" aria-label="タスク名" />
<SubmitButton />
{state.error && <p role="alert">{state.error}</p>}
<ul id="tasks">
{optimisticTasks.map((t) => (
<li key={t.id}>{t.id < 0 ? `${t.title}(保存中)` : t.title}</li>
))}
</ul>
</form>
)
}
use()・ref as prop・Context as provider・title・Activity
// src/App.tsx
import { Activity, Suspense, createContext, use, useRef, useState } from 'react'
import { fetchTasks } from './api'
import { TaskForm } from './TaskForm'
import { Products } from './Products'
// <Context> をそのまま Provider として使える(.Provider 不要)
const ThemeContext = createContext('light')
// ref を普通の props として受け取れる(forwardRef 不要)
function SearchInput({ ref }: { ref: React.Ref<HTMLInputElement> }) {
return <input ref={ref} aria-label="検索" placeholder="検索" />
}
function ThemeLabel() {
const theme = use(ThemeContext) // use() は Context も読める
return <p id="theme">テーマ: {theme}</p>
}
// use(promise):Suspense と組み合わせて Promise の結果を読む
const tasksPromise = fetchTasks()
function Tasks() {
const tasks = use(tasksPromise)
return <TaskForm initialTasks={tasks} />
}
export function App() {
const [tab, setTab] = useState<'tasks' | 'products'>('tasks')
const inputRef = useRef<HTMLInputElement>(null)
return (
<ThemeContext value="dark">
{/* <title> をコンポーネント内に書くと <head> に反映される */}
<title>{tab === 'tasks' ? 'タスク一覧' : '商品一覧'}</title>
<ThemeLabel />
<SearchInput ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>検索欄にフォーカス</button>
<nav>
<button onClick={() => setTab('tasks')}>タスク</button>
<button onClick={() => setTab('products')}>商品</button>
</nav>
{/* Activity:非表示でも状態を保ったまま隠す */}
<Activity mode={tab === 'tasks' ? 'visible' : 'hidden'}>
<Suspense fallback={<p id="loading">読み込み中…</p>}>
<Tasks />
</Suspense>
</Activity>
<Activity mode={tab === 'products' ? 'visible' : 'hidden'}>
<Products />
</Activity>
</ThemeContext>
)
}
use(promise) に渡す Promise は、描画のたびに作り直さないようにします。ここではモジュールの最上位で1回だけ作っています。実際のアプリでは、フレームワークやデータ取得ライブラリがキャッシュした Promise を渡すのが一般的です。
Playwright で操作した結果
npx vite build → npx vite preview で起動し、Playwright で順に操作してテキストを取得しました。
[use] +80ms loading表示: true
[use] +930ms タスク: [ '備品の棚卸し' ]
[title] タスク一覧
[context] テーマ: dark
[ref] focused aria-label = 検索
[action] エラー: 2文字以上で入力してください
[optimistic] 直後 ボタン: 送信中… / 一覧: [ '備品の棚卸し', '会議室の予約(保存中)' ] / 入力欄: "会議室の予約"
[optimistic] 完了後 ボタン: 追加 / 入力欄: "" / 一覧: [ '備品の棚卸し', '会議室の予約' ]
[title] 商品タブ: 商品一覧
[activity] 商品タブの表示状態: false
[activity] 戻った後のメモ: "abcde"
[console errors/warnings] []
use(promise) は約0.9秒後に一覧を表示し、<title> はタブ切り替えで書き換わり、ref as prop で親から子の入力欄にフォーカスできました。送信直後は「(保存中)」の仮の行とボタン「送信中…」が出て、完了後に確定し、入力欄は自動でリセットされています。<Activity mode="hidden"> で隠した商品タブの入力値「abcde」も保たれました。
useOptimistic の仮の値は、Action が終わると自動で消え、確定した state の表示に置き換わります。自分で「保存中の行を消す」処理を書かなくてよいのがポイントです。
19.2・19.3 で増えた機能の使いどころ
Activity(19.2)
<Activity mode="hidden"> は、画面から隠しつつ state を保つ部品です。条件分岐({tab === 'x' && <X />})で消すと state は失われますが、Activity なら上の実行結果のとおり入力値が残ります。公式ブログでは、hidden のあいだは Effect を片付け、更新を後回しにすると説明されています。
| 書き方 | 非表示中の state | Effect | 向いている場面 |
|---|---|---|---|
{show && <X />} |
消える | 片付けられる | 二度と使わない画面 |
<Activity mode="hidden"> |
保たれる | 片付けられる | タブ、戻る操作、次に開く画面の先読み |
CSS の display: none |
保たれる | 動き続ける | Effect を止めたくない特殊な場合 |
useEffectEvent(19.2)
Effect の中で「最新の値は読みたいが、その値が変わっても Effect を再実行したくない」ときに使います。依存配列には入れないのがルールです(公式ブログの例を短くしたもの)。
const onConnected = useEffectEvent(() => {
showNotification('接続しました', theme) // 常に最新の theme を読む
})
useEffect(() => {
// 接続処理の中で onConnected() を呼ぶ
}, [roomId]) // theme が変わっても再接続しない
ViewTransition と addTransitionType(19.3 で安定版)
startTransition(() => {
addTransitionType('next') // 遷移の種類を付ける
setCurrentSlide((c) => c + 1)
})
<ViewTransition enter={{ next: 'from-right' }} exit={{ next: 'to-left' }}>
<Page />
</ViewTransition>
startTransition・<Suspense> の表示切り替え・useDeferredValue による更新で、ブラウザの View Transition API を使ったアニメーションが動きます。上のコードは公式ブログの例を短くしたもので、この記事のサンプルでは実行していません。
browser()(19.3、react-dom)
import { use } from 'react'
import { browser } from 'react-dom'
function TimeZone() {
use(browser()) // サーバーでは Suspense の fallback を出し、ブラウザで描画
const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone
return <p>{timeZone}</p>
}
localStorage やタイムゾーンなど、ブラウザでしか値が決まらない部品を SSR から外す公式の方法です。これまでの「useEffect でマウント後にフラグを立てる」書き方を置き換えられます。
React Compiler 1.0:useMemo を書かずにメモ化する
React Compiler は、ビルド時にコンポーネントを解析し、useMemo / useCallback / memo に相当するキャッシュを自動で入れるツールです。2025年10月7日に 1.0 が安定版になり、React 17 以降に対応しています(17・18 は react-compiler-runtime の追加と設定が必要)。公式ブログでは、Meta の本番環境で初回表示やページ遷移が最大12%改善、一部の操作は2.5倍以上速くなったと紹介されています。
導入方法
| 環境 | 設定 |
|---|---|
| Vite 8(@vitejs/plugin-react 6) | @rolldown/plugin-babel + reactCompilerPreset()(下記) |
| Next.js 16 | next.config.ts に reactCompiler: true(babel-plugin-react-compiler のインストールが必要) |
| ESLint | eslint-plugin-react-hooks@latest の recommended にコンパイラ用ルールが含まれる |
// vite.config.ts
import { defineConfig } from 'vite'
import react, { reactCompilerPreset } from '@vitejs/plugin-react'
import babel from '@rolldown/plugin-babel'
// COMPILER=off で React Compiler なしのビルドを作る(比較用)
const useCompiler = process.env.COMPILER !== 'off'
export default defineConfig({
plugins: [
react(),
...(useCompiler ? [babel({ presets: [reactCompilerPreset()] })] : []),
],
build: { minify: false, outDir: useCompiler ? 'dist' : 'dist-nocompiler' },
})
@vitejs/plugin-react 6 では、以前の react({ babel: { plugins: [...] } }) ではなく、@rolldown/plugin-babel と reactCompilerPreset を組み合わせる形が README で案内されています。README には Rust 製コンパイラを使う react({ compiler: true }) もありますが、こちらは実験的と明記されています。
useMemo を書いていないコンポーネントで比べる
// src/Products.tsx
import { useState } from 'react'
const products = Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `商品${i}`, price: (i * 37) % 1000 }))
// 計測用:フィルター処理が何回実行されたか
declare global { interface Window { filterCount: number; rowRender: number } }
window.filterCount = 0
window.rowRender = 0
function filterProducts(max: number) {
window.filterCount++
return products.filter((p) => p.price <= max)
}
function ProductList({ items }: { items: { id: number; name: string }[] }) {
window.rowRender++
return <p id="count">{items.length}件</p>
}
// useMemo / memo を一切書いていないコンポーネント
export function Products() {
const [max, setMax] = useState(100)
const [memo, setMemo] = useState('')
const items = filterProducts(max)
return (
<section>
<input aria-label="メモ" value={memo} onChange={(e) => setMemo(e.target.value)} />
<button onClick={() => setMax((m) => m + 100)}>上限+100</button>
<ProductList items={items} />
</section>
)
}
メモ欄に5文字入力したとき、関係のないフィルター処理と子の描画が何回走ったかを数えました。
== compiler ON
[compiler] メモに5文字入力: filterProducts 0回 / ProductList描画 0回
[compiler] 上限+100: filterProducts 1回 / ProductList描画 1回 / 表示 201件
== compiler OFF
[compiler] メモに5文字入力: filterProducts 5回 / ProductList描画 5回
[compiler] 上限+100: filterProducts 1回 / ProductList描画 1回 / 表示 201件
コンパイラなしでは1文字ごとにフィルターと子の描画が走り(5回)、コンパイラありでは0回になりました。上限を変えたときは、どちらも必要な1回だけ実行されています。
ビルド出力はこう変わる
minify: false でビルドし、Products 関数の出力を比べました(コンパイラあり、抜粋)。
function Products() {
const $ = (0, import_compiler_runtime.c)(11);
const [max, setMax] = (0, import_react.useState)(100);
const [memo, setMemo] = (0, import_react.useState)("");
let t0;
if ($[0] !== max) {
t0 = filterProducts(max);
$[0] = max;
$[1] = t0;
} else t0 = $[1];
const items = t0;
// ...(中略)...
let t4;
if ($[6] !== items) {
t4 = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProductList, { items });
$[6] = items;
$[7] = t4;
} else t4 = $[7];
// ...
return t5;
}
コンパイラなしの出力は、元のコードとほぼ同じ(const items = filterProducts(max); がそのまま毎回実行される形)でした。
コンパイラは $(長さ11のキャッシュ配列)を用意し、「max が前回と同じなら前回の結果を使う」という比較を自動で書き足しています。これは useMemo(() => filterProducts(max), [max]) と memo(ProductList) を手で書いたのと同じ効果です。バンドルサイズは 571.25 kB → 574.60 kB(未圧縮)で、キャッシュ処理のぶん少し増えました。
公式ブログでは、既存の useMemo / useCallback は残してよく、外すなら十分にテストすること、Effect の依存など細かく制御したい箇所では手書きを併用することが案内されています。
React 18 から 19 への移行で変わる点
公式アップグレードガイドの内容を一覧にしました。まず npx codemod@latest react/19/migration-recipe と npx types-react-codemod@latest preset-19 ./src を実行すると、多くを自動で書き換えられます。
削除・非推奨になったAPI
| 18までの書き方 | 19での扱い | 置き換え先 |
|---|---|---|
ReactDOM.render / ReactDOM.hydrate |
削除 | createRoot / hydrateRoot |
unmountComponentAtNode |
削除 | root.unmount() |
ReactDOM.findDOMNode |
削除 | useRef で DOM を参照 |
関数コンポーネントの propTypes / defaultProps |
削除(propTypes は無視される) |
TypeScript・引数のデフォルト値 |
文字列 ref(ref="input") |
削除 | ref コールバック・useRef |
Legacy Context(contextTypes) |
削除 | createContext |
React.createFactory / モジュールパターン |
削除 | JSX・通常の関数 |
react-dom/test-utils |
削除 | react の act |
react-test-renderer/shallow |
削除 | react-shallow-renderer |
| UMD ビルド | 削除 | ESM の CDN |
element.ref |
非推奨 | element.props.ref |
react-test-renderer |
非推奨 | @testing-library/react |
<Context.Provider> / forwardRef |
動くが今後非推奨の予定 | <Context> / ref as prop |
動作の変更
| 変更 | 内容 |
|---|---|
| 新しい JSX トランスフォームが必須 | jsx: "react-jsx" など新方式の設定が必要 |
| 描画中のエラーを再スローしない | 捕捉されないエラーは window.reportError、捕捉済みは console.error へ。onUncaughtError / onCaughtError で変更可 |
| StrictMode の変更 | 開発時の二重描画で、useMemo / useCallback の1回目の結果を再利用 |
| Suspense の改善 | 中断した部品の兄弟を待たずに fallback を先に出す |
| React の内部に依存するライブラリ | 更新されるまで移行できない場合がある |
TypeScript で実際に出たエラー
React 18 時代によくある書き方を、@types/react 19.3.0 / TypeScript 5.9.3 で型チェックしました。
// mig/legacy.tsx
import { useRef } from 'react'
import ReactDOM from 'react-dom'
export function Legacy() {
const ref = useRef<HTMLDivElement>()
return <div ref={(el) => (ref.current = el!)} />
}
ReactDOM.render(<Legacy />, document.getElementById('root'))
const el: JSX.Element = <div />
mig/legacy.tsx(5,15): error TS2554: Expected 1 arguments, but got 0.
mig/legacy.tsx(6,15): error TS2322: Type '(el: HTMLDivElement | null) => HTMLDivElement' is not assignable to type 'Ref<HTMLDivElement> | undefined'.
Type '(el: HTMLDivElement | null) => HTMLDivElement' is not assignable to type '(instance: HTMLDivElement | null) => void | (() => VoidOrUndefinedOnly)'.
Type 'HTMLDivElement' is not assignable to type 'void | (() => VoidOrUndefinedOnly)'.
mig/legacy.tsx(9,10): error TS2339: Property 'render' does not exist on type 'typeof import(".../node_modules/@types/react-dom/index")'.
mig/legacy.tsx(10,11): error TS2503: Cannot find namespace 'JSX'.
実行時にも require('react-dom') に render / findDOMNode / unmountComponentAtNode は存在しませんでした(typeof がすべて undefined)。
| エラー | 原因 | 直し方 |
|---|---|---|
| TS2554 Expected 1 arguments | useRef の引数が必須に |
useRef<HTMLDivElement>(null) |
| TS2322(ref コールバック) | アロー関数が代入結果を暗黙に返している。19では戻り値がクリーンアップ関数とみなされる | ref={(el) => { ref.current = el }} と波かっこで囲む |
| TS2339 'render' | ReactDOM.render の削除 |
createRoot(el).render(<App />) |
| TS2503 namespace 'JSX' | グローバルの JSX 名前空間の削除 |
React.JSX.Element |
移行チェックリスト
| チェック項目 | 確認 |
|---|---|
react react-dom @types/react @types/react-dom をそろえて 19 系にした |
□ |
react/19/migration-recipe と types-react-codemod preset-19 を実行した |
□ |
ReactDOM.render を createRoot に置き換えた |
□ |
useRef() に引数を渡した |
□ |
| ref コールバックが値を暗黙に返していない | □ |
| React Compiler を試すなら ESLint の react-hooks ルールも入れた | □ |
よくある質問
Q. React 19 に上げたら useMemo と useCallback は消すべきですか?
A. React 19 に上げただけでは自動メモ化は入りません。自動化されるのは React Compiler を導入したときです。公式ブログでは、既存の useMemo / useCallback は残しておくか、外すなら十分にテストするよう案内されています。新しく書くコードはコンパイラに任せ、必要な箇所だけ手で書くのがおすすめです。
Q. useActionState と useFormStatus はどう使い分けますか?
A. useActionState は Action の結果(state)を持つフックで、フォームを持つ部品で使います。useFormStatus は react-dom のフックで、子の部品から親の <form> の送信中状態を読むために使います。送信ボタンを別部品にしたいときに便利です。
Q. React 19.3 に破壊的変更はありますか?
A. 19.3 の公式ブログには破壊的変更の記載はありません。ViewTransition と Fragment refs の安定化、browser() や Trusted Types 対応などの追加が中心です。細かな変更として、Strict Mode のハイドレーション中にも Effect が二重に実行されるようになった点が挙げられています。
まとめ
- React 19 の中心は Actions。
useActionState・useOptimistic・useFormStatus・<form action>で送信処理が短く書ける use()・ref as prop・<Context>as provider・<title>の直接記述で、定番の書き方がシンプルになった- 19.2 で
<Activity>・useEffectEvent、19.3 で<ViewTransition>・Fragment refs・browser()が安定版に - React Compiler 1.0 を入れると、
useMemoなしでも不要な再計算が5回→0回になった(実測) - ビルド出力には
$のキャッシュ配列と比較処理が自動で入る - 移行では
useRef()の引数・ref コールバックの戻り値・ReactDOM.render・JSX名前空間が型エラーになりやすい - codemod を先に実行すると、多くの変更を自動で書き換えられる
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「React 19の新機能まとめ|19.3までの一覧とCompilerを実測」
株式会社RJCでは、React の新しい機能を実際のコードで試しながら、プロジェクトの中で仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。