- Vue.js
Vueのpropsとemitの使い方|親子のデータ受け渡しを実行結果で解説
propsとemitとは、Vueで「親から子へ値を渡す(props)」「子から親へ出来事を知らせる(emit)」ための仕組みです。 データは常に親から子へ一方向に流れ、子は値を直接書き換えずに emit で親へ「変えてほしい」と伝えます。この2つを押さえると、コンポーネント間のデータ受け渡しの大部分を書けるようになります。
この記事では、Vue 3.5 の <script setup> + TypeScript を前提に、コピペで動く defineProps / defineEmits の書き方を紹介します。さらに、子で props を書き換えたときにブラウザに実際に出る警告文、vue-tsc(Vue用の型チェッカー)が出す型エラー、Playwright で確認した親子の値の受け渡しを載せています。兄弟・孫コンポーネントへの渡し方と、props / emit / v-model / provide・inject / Pinia の「どれを使うか」の判断表もまとめました。
propsとemitの早見表
| やりたいこと | 書き方(Vue 3.5 + TS) | ポイント |
|---|---|---|
| 親→子に値を渡す | 親:<Child :task="t" /> 子:defineProps<{ task: Task }>() |
値は :(v-bind)で渡す。付けないと文字列になる |
| 任意のpropsに既定値 | const { label = '完了' } = defineProps<{ label?: string }>() |
3.5 のリアクティブな分割代入。3.4以前は withDefaults |
| 必須にする | 型で ? を付けない(task: Task) |
渡し忘れは vue-tsc と実行時警告の両方で検出 |
| 真偽値を属性だけで渡す | 親:<Child show-id /> |
Boolean 型は属性があるだけで true |
| 子→親に知らせる | 子:emit('toggle', id) 親:@toggle="onToggle" |
型は defineEmits<{ toggle: [id: number] }>() |
| emitの引数を実行時に検証 | defineEmits({ submit: (p) => p.email.includes('@') }) |
検証に失敗してもイベント自体は届く(警告のみ) |
| 名前の書き方 | 宣言:showId(キャメル) 親:show-id(ケバブ) |
どちらで書いても同じpropsとして届く |
| 親子で双方向に同期 | defineModel() + 親で v-model |
内部は props + update:xxx の emit |
| 孫以下へまとめて渡す | provide(key, value) / inject(key) |
中間コンポーネントを経由しなくてよい |
| アプリ全体で共有 | Pinia のストア | 画面をまたぐ状態向け |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Vueのpropsとemitの使い方|親子のデータ受け渡しを実行結果で解説」
検証した環境
この記事のコードと実行結果は、次の環境で実際に動かして確認したものです。
| 項目 | バージョン |
|---|---|
| Vue | 3.5.43 |
| Vite / @vitejs/plugin-vue | 8.3.1 / 6.0.9 |
| vue-tsc / TypeScript | 3.3.11 / 6.0.3 |
| Playwright(Chromium) | 1.56.1 |
| Node.js | 22.22.2 |
vue-tsc 3.3 は TypeScript 7(Go 実装)では動かないため、TypeScript 6 系を入れています。 npm i -D typescript だと最新の 7 系が入るので、typescript@6 のようにバージョンを指定すると確実です。
親から子へ値を渡す:definePropsの基本
propsは「子が受け取る値の宣言」です。 <script setup> では defineProps を使い、TypeScript の型で宣言するのがいちばん簡潔です。defineProps は import 不要のコンパイラマクロ(ビルド時に変換される特別な関数)です。
子コンポーネント(TaskItem.vue)
// types.ts
import type { InjectionKey, Ref } from 'vue'
export interface Task { id: number; title: string; done: boolean }
export const selectedKey: InjectionKey<Ref<number | null>> = Symbol('selected')
<!-- TaskItem.vue -->
<script setup lang="ts">
import type { Task } from './types'
// 型ベースの宣言 + 3.5 のリアクティブな分割代入(既定値もここで書ける)
const { task, showId = false, label = '完了にする' } = defineProps<{
task: Task // 必須
showId?: boolean // 任意(真偽値)
label?: string // 任意(既定値あり)
}>()
// 型付きの emit(3.3+ の名前付きタプル構文)
const emit = defineEmits<{
toggle: [id: number]
rename: [id: number, title: string]
}>()
function onRename() {
emit('rename', task.id, task.title + '(改)')
}
</script>
<template>
<li :data-testid="`task-${task.id}`" :class="{ done: task.done }">
<span v-if="showId" class="id">#{{ task.id }}</span>
<span class="title">{{ task.title }}</span>
<button class="toggle" @click="emit('toggle', task.id)">
{{ task.done ? '未完了に戻す' : label }}
</button>
<button class="rename" @click="onRename">名前を変える</button>
</li>
</template>
親コンポーネント(ParentPage.vue)
<script setup lang="ts">
import { ref, computed, provide } from 'vue'
import TaskItem from './TaskItem.vue'
import TaskCounter from './TaskCounter.vue'
import TaskPanel from './TaskPanel.vue'
import { selectedKey, type Task } from './types'
const tasks = ref<Task[]>([
{ id: 1, title: '備品の発注', done: false },
{ id: 2, title: '議事録の共有', done: true },
])
const doneCount = computed(() => tasks.value.filter(t => t.done).length)
const selected = ref<number | null>(null)
provide(selectedKey, selected)
function toggle(id: number) {
const t = tasks.value.find(t => t.id === id)
if (t) t.done = !t.done
selected.value = id
}
function rename(id: number, title: string) {
const t = tasks.value.find(t => t.id === id)
if (t) t.title = title
}
</script>
<template>
<ul>
<TaskItem
v-for="t in tasks"
:key="t.id"
:task="t"
show-id
@toggle="toggle"
@rename="rename"
/>
</ul>
<TaskCounter :total="tasks.length" :done="doneCount" />
<TaskPanel />
</template>
データを持っているのは親の tasks だけで、子は表示とボタンの通知だけを担当します。 これが「一方向データフロー」です。TaskCounter と TaskPanel は後の「兄弟・孫への渡し方」で使います。
3.5のリアクティブな分割代入と既定値
Vue 3.5 から、defineProps の戻り値を分割代入しても値がリアクティブ(変更が画面に反映される状態)のまま保たれます。 コンパイラが task を内部で props.task に書き換えるためです。既定値も JavaScript の分割代入の既定値としてそのまま書けます。
| 書き方 | 対応バージョン | 既定値の書き方 |
|---|---|---|
const { label = 'x' } = defineProps<{ label?: string }>() |
3.5 以降 | 分割代入の既定値 |
const props = withDefaults(defineProps<Props>(), { label: 'x' }) |
3.3〜(3.5でも使える) | 第2引数のオブジェクト |
defineProps({ label: { type: String, default: 'x' } }) |
全バージョン(実行時宣言) | default オプション |
分割代入した値を watch に渡すときだけは、getter(値を返す関数)で包む必要があります。
const { task } = defineProps<{ task: Task }>()
watch(() => task.done, (v) => console.log('完了状態:', v)) // ○ getter で包む
// watch(task.done, ...) と書くと、その時点の値を渡すだけになり、変化を追えない
withDefaults で配列やオブジェクトの既定値を書くときは、関数で返します(labels: () => ['A', 'B'])。インスタンス間で同じ配列を共有しないためです。分割代入の既定値では、書いた式が毎回評価されるのでこの配慮は不要です。
キャメルケースとケバブケース
宣言はキャメルケース(showId)、親のテンプレートではケバブケース(show-id)で書くのが公式の推奨です。 Vue はどちらの書き方でも同じ props として扱うので、<TaskItem showId /> でも動きます。HTML の属性名の慣習に合わせてケバブケースにそろえると、読みやすさが上がります。
真偽値の省略・必須と任意
| 親の書き方 | 子で受け取る値(showId?: boolean) |
|---|---|
<TaskItem show-id /> |
true |
<TaskItem :show-id="false" /> |
false |
| 何も書かない | false(Boolean型は未指定で false になる) |
<TaskItem show-id="false" /> |
文字列の "false"(型チェックの警告が出る) |
Playwright で5通りの渡し方を表示させた結果です(子は {{ typeof showId }}:{{ showId }} を表示)。
[Vue warn]: Invalid prop: type check failed for prop "showId". Expected Boolean, got String with value "false".
[ 'boolean:true', 'boolean:false', 'boolean:false', 'string:false', 'boolean:true' ]
順に show-id、:show-id="false"、指定なし、show-id="false"、show-id="show-id" の結果です。Boolean 型は、HTML の disabled のように「属性があれば(値が空か props 名と同じなら)true」になります。 show-id="false" は : がないので文字列のまま届き、if 文では真として扱われてしまいます。false を渡したいときは :show-id="false" と : を付けます。
必須と任意は型の ? で決まります。
| 型の書き方 | 意味 | 渡し忘れたとき |
|---|---|---|
task: Task |
必須 | vue-tsc でエラー、実行時に Missing required prop 警告 |
label?: string |
任意 | 既定値(なければ undefined) |
showId?: boolean |
任意 | false |
子から親へ知らせる:defineEmitsの基本
emitは「子で起きた出来事を、引数つきで親へ知らせる」仕組みです。 親は @イベント名 で受け取って、自分のデータを更新します。
// 3.3+ の名前付きタプル構文(おすすめ)
const emit = defineEmits<{
toggle: [id: number]
rename: [id: number, title: string]
}>()
// 同じ意味の関数シグネチャ構文
const emit2 = defineEmits<{
(e: 'toggle', id: number): void
(e: 'rename', id: number, title: string): void
}>()
型で宣言すると、emit('toggle', '1') のような引数の型違いや、宣言していないイベント名を vue-tsc が検出します(後の「vue-tscで型エラーを出してみる」で実例を載せています)。テンプレートの中では $emit('toggle', task.id) とも書けますが、<script> から呼ぶ場合と書き方をそろえるなら emit を使うとわかりやすいです。
親子の受け渡しをPlaywrightで確認
上の ParentPage を Vite の開発サーバーで表示し、Playwright で子のボタンを押して親・兄弟・孫の表示を読みました。
await page.goto('http://localhost:7420/#props/ParentPage')
console.log('初期:', await t('task-1'), '|', await t('counter'), '|', await t('badge'))
await page.getByTestId('task-1').locator('.toggle').click()
console.log('子でtoggle後:', await t('task-1'), '|', await t('counter'), '|', await t('badge'))
await page.getByTestId('task-2').locator('.rename').click()
console.log('子でrename後:', await t('task-2'))
実行結果:
初期: #1備品の発注完了にする名前を変える | 完了 1 / 2 | 選択中: -
子でtoggle後: #1備品の発注未完了に戻す名前を変える | 完了 2 / 2 | 選択中: 1
子でrename後: #2議事録の共有(改)未完了に戻す名前を変える
ParentPageの警告数: 0
子の emit → 親の tasks が更新 → props 経由で子・兄弟(完了数)・孫(選択中)が再描画、という流れが確認できます。show-id と書いただけで #1 が表示されているので、真偽値の省略記法も効いています。
propsを子で書き換えるとどうなるか(実際の警告文)
propsは子から見て読み取り専用です。 実際に書き換えるコードを動かし、ブラウザのコンソールを確認しました。
<!-- Counter.vue(あえて書き換える例) -->
<script setup lang="ts">
const props = defineProps<{ count: number; item: { name: string } }>()
function bump() {
props.count++ // ← props を直接書き換える
}
function renameItem() {
props.item.name = '書き換え済み' // ← オブジェクトの中身を書き換える
}
</script>
<template>
<p data-testid="count">{{ count }}</p>
<p data-testid="item">{{ item.name }}</p>
<button class="bump" @click="bump">+1</button>
<button class="rename" @click="renameItem">中身を書き換え</button>
</template>
実行結果(ボタンを押したあとの表示とコンソール):
bump後の表示: 1
中身書き換え後 子: 書き換え済み / 親: 親: 書き換え済み
[warning] [Vue warn] Set operation on key "count" failed: target is readonly. Proxy(Object)
| 書き換え方 | 結果 | 警告 |
|---|---|---|
props.count++(props そのもの) |
値は変わらない(表示は 1 のまま) | Set operation on key "count" failed: target is readonly. |
props.item.name = ...(オブジェクトの中身) |
親の値まで変わる | 出ない |
オブジェクトや配列の中身は書き換えられてしまい、しかも警告が出ません。 親と子が同じオブジェクトを参照しているためです。公式ドキュメントでも「できるが、親子の結びつきが強くなりデータの流れが追いにくくなるので、emit で親に更新を任せるのがよい」と説明されています。
直す前と後
| 目的 | 直す前 | 直した後 |
|---|---|---|
| 親の値を変えたい | props.item.name = v |
emit('rename', v) → 親で更新 |
| 初期値として使い、子の中で自由に変えたい | props.count++ |
const local = ref(count) にコピーして local.value++ |
| propsから計算した値を表示したい | props を加工して上書き | computed(() => count * 2) |
TypeScript で書いていれば、props.count++ は vue-tsc が Cannot assign to 'count' because it is a read-only property. として検出します。
emitの引数を実行時に検証する
実行時の検証が必要なら、defineEmits にオブジェクトで検証関数を渡します。 戻り値が false だと警告が出ます。
<!-- Signup.vue -->
<script setup lang="ts">
const emit = defineEmits({
submit: (payload: { email: string }) => payload.email.includes('@'),
})
function send(email: string) {
emit('submit', { email })
}
</script>
<template>
<button class="ok" @click="send('a@example.com')">正しい値</button>
<button class="ng" @click="send('abc')">不正な値</button>
</template>
親は <Signup @submit="(p) => console.log('受信:', p.email)" /> としました。実行結果:
[log] 受信: a@example.com
[warning] [Vue warn]: Invalid event arguments: event validation failed for event "submit".
[log] 受信: abc
検証に失敗しても、イベントは親に届きます(受信: abc が出ている)。検証関数は開発中の気づきのためのもので、処理を止める仕組みではありません。入力チェックそのものは、送る前に子で行うか、受け取った親で行いましょう。
宣言していないイベントを発火した場合も、開発時に警告が出ます。
[warning] [Vue warn]: Component emitted event "cancel" but it is neither declared in the emits option nor as an "onCancel" prop.
親が渡す値を間違えたときの実行時警告
親側の間違いも、開発モードではコンソールに出ます。
<Counter count="1" :item="item" /> <!-- : を付け忘れて文字列を渡した -->
<Counter :item="item" /> <!-- 必須の count を渡し忘れた -->
[warning] [Vue warn]: Invalid prop: type check failed for prop "count". Expected Number with value 1, got String with value "1".
[warning] [Vue warn]: Missing required prop: "count"
count="1" は文字列、:count="1" は数値です。 数値・真偽値・オブジェクトを渡すときは : を付けます。型ベースの宣言でも、Vue は実行時チェック用の情報(type: Number など)を自動で生成するので、このような警告が出ます。
vue-tscで型エラーを出してみる
ブラウザで動かす前に間違いを見つけるなら、vue-tsc で型チェックします。 わざと間違えたファイルで確認しました。
<!-- TypeErrors.vue -->
<script setup lang="ts">
import TaskItem from '../props/TaskItem.vue'
const emit = defineEmits<{ saved: [id: number] }>()
emit('saved', '1') // 引数の型違い
emit('deleted', 1) // 宣言していないイベント
</script>
<template>
<!-- 1. 必須 props の渡し忘れ -->
<TaskItem />
<!-- 2. 型違い(done が文字列) -->
<TaskItem :task="{ id: 1, title: 'A', done: 'yes' }" />
<!-- 3. 子が宣言していないイベントを購読 -->
<TaskItem :task="{ id: 1, title: 'A', done: false }" @remove="() => {}" />
<!-- 4. イベント引数の型を取り違え -->
<TaskItem :task="{ id: 1, title: 'A', done: false }" @toggle="(id: string) => {}" />
</template>
npx vue-tsc --noEmit
実行結果(長い型の部分は一部省略):
TypeErrors.vue(5,15): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
TypeErrors.vue(6,6): error TS2345: Argument of type '"deleted"' is not assignable to parameter of type '"saved"'.
TypeErrors.vue(11,4): error TS2345: Argument of type '{}' is not assignable to parameter of type '{ readonly task: Task; ... }'.
Property 'task' is missing in type '{}' but required in type '{ readonly task: Task; ... }'.
TypeErrors.vue(13,41): error TS2322: Type 'string' is not assignable to type 'boolean'.
TypeErrors.vue(17,57): error TS2322: Type '(id: string) => void' is not assignable to type '(id: number) => any'.
「3. 宣言していないイベントの購読(@remove)」は、既定の設定では検出されませんでした。 未知のイベントは通常の属性として子に引き継がれる(フォールスルー属性)ためです。tsconfig.json に strictTemplates を足すと検出されます。
{
"vueCompilerOptions": { "strictTemplates": true }
}
TypeErrors.vue(15,57): error TS2353: Object literal may only specify known properties, and 'onRemove' does not exist in type ...
strictTemplates は未知の属性やコンポーネントもまとめて厳しく見るため、この環境では <li :data-testid="..."> もエラーとして報告されました。 既存のプロジェクトに入れるときは、出たエラーを一覧で確認してから有効にすると進めやすいです。
| 間違い | vue-tsc(既定) | vue-tsc(strictTemplates) | 実行時の警告 |
|---|---|---|---|
| 必須propsの渡し忘れ | 検出 | 検出 | Missing required prop |
| propsの型違い | 検出 | 検出 | Invalid prop: type check failed |
| propsの書き換え | 検出(read-only) | 検出 | target is readonly |
| 未宣言イベントの発火(子) | 検出 | 検出 | neither declared in the emits option |
| 未宣言イベントの購読(親) | 検出されない | 検出 | 出ない |
| emit引数の型違い | 検出 | 検出 | 検証関数があれば Invalid event arguments |
親子以外への渡し方:兄弟・孫
兄弟コンポーネント:状態を親に持ち上げる
兄弟同士は直接やり取りせず、共通の親が状態を持ちます。 片方が emit で親に知らせ、親が props でもう片方に渡します。前述の ParentPage では、TaskItem の emit で親の tasks が変わり、兄弟の TaskCounter が props で完了数を受け取っていました。
<!-- TaskCounter.vue:親が持つ状態を props で受け取るだけ -->
<script setup lang="ts">
const { total, done } = defineProps<{ total: number; done: number }>()
</script>
<template>
<p data-testid="counter">完了 {{ done }} / {{ total }}</p>
</template>
flowchart TB
P["親 ParentPage(tasks を持つ)"]
A["子 TaskItem"]
B["兄弟 TaskCounter"]
C["中間 TaskPanel"]
G["孫 SelectedBadge"]
A -- "emit('toggle', id)" --> P
P -- "props :task" --> A
P -- "props :done :total" --> B
P --> C --> G
P -. "provide(selectedKey)" .-> G
孫以下:provide / inject
2階層以上離れたコンポーネントに渡すときは、provide / inject を使うと中間のコンポーネントに props を書かずに済みます。 型付きのキー(InjectionKey)を別ファイルに置くと、受け取る側でも型が付きます。
<!-- SelectedBadge.vue(孫) -->
<script setup lang="ts">
import { inject } from 'vue'
import { selectedKey } from './types'
const selected = inject(selectedKey) // 型は Ref<number | null> | undefined
</script>
<template>
<span data-testid="badge">選択中: {{ selected ?? '-' }}</span>
</template>
<!-- TaskPanel.vue(中間):何も受け渡さない -->
<script setup lang="ts">
import SelectedBadge from './SelectedBadge.vue'
</script>
<template>
<section class="panel"><SelectedBadge /></section>
</template>
Playwright の結果で 選択中: 1 と表示されたのが、この経路です。値を変えるのは provide した親だけにして、孫で変えたい場合は更新用の関数も一緒に provide すると、どこで値が変わるかが追いやすくなります。
props・emit・v-model・provide/inject・Piniaの使い分け
| 方法 | 向いている場面 | 向いていない場面 | 型付け |
|---|---|---|---|
| props | 親→子に表示用の値を渡す | 3階層以上のバケツリレー | defineProps<{...}>() |
| emit | 子の操作を親に知らせる | 遠いコンポーネントへの通知 | defineEmits<{...}>() |
| v-model(defineModel) | 入力部品など、親子で1つの値を同期 | 複数の値をまとめて管理 | defineModel<string>() |
| provide / inject | テーマ・設定・フォーム全体の状態を子孫へ | 画面をまたぐ共有 | InjectionKey<T> |
| Pinia | ログイン中の会員情報、カートなど画面をまたぐ状態 | 1コンポーネントで完結する値 | ストアの定義から推論 |
迷ったときの順番は次のとおりです。
- 親子だけで完結する → props + emit
- 入力部品として1つの値を同期したい → v-model(defineModel)
- 間に2階層以上ある、または同じ値を多くの子孫が使う → provide / inject
- 別のページやルートでも同じ値を使う → Pinia
v-model は modelValue の props と update:modelValue の emit の組み合わせを短く書く仕組みで、defineModel() を使うと子の宣言が1行になります。Pinia はアプリ全体の状態管理ライブラリです。どちらも詳しい使い方は個別のテーマなので、ここでは選び方の目安にとどめます。
よくある失敗と直し方
| 症状 | 原因 | 直し方 |
|---|---|---|
| 数値を渡したのに文字列になる | count="1" と : を付け忘れ |
:count="1" |
false を渡したのに条件が真になる |
show-id="false" は文字列 "false" として届く |
:show-id="false" |
| 分割代入したpropsを watch しても反応しない | 値そのものを渡している | watch(() => task, ...) |
| 親の配列が子の操作で勝手に変わる | 子でオブジェクトの中身を書き換えている | emit で親に更新を依頼 |
親の @remove が呼ばれない |
子の emits に宣言がない、名前の食い違い | 子の defineEmits を確認。strictTemplates で検出 |
| emit の検証に失敗しても処理が進む | 検証関数は警告を出すだけ | 送る前・受け取った後にチェック |
withDefaults の配列既定値が共有される |
オブジェクトを直接書いている | () => [] のように関数で返す |
よくある質問
Q. definePropsとwithDefaultsはどちらを使えばよいですか?
A. Vue 3.5 以降なら、分割代入の既定値(const { label = 'x' } = defineProps<...>())がいちばん短く書けます。3.4 以前のプロジェクトや、props.label の形でまとめて扱いたい場合は withDefaults を使います。どちらも型は同じように付きます。
Q. emitで複数の値を渡せますか?
A. 渡せます。rename: [id: number, title: string] のように宣言し、emit('rename', 1, '新しい名前') と呼びます。値が多いときは { id, title } のようにオブジェクト1つにまとめると、引数の順番を気にせずに済みます。
Q. 子から親の関数を直接呼んではいけませんか?
A. 関数を props で渡すこともできますが、Vue では emit で知らせるのが一般的です。emit にしておくと、親の @イベント名 を見るだけで「子から何が起きるか」がわかり、vue-tsc による型チェックも効きます。
Q. Options APIのprops/emitsと混在できますか?
A. できます。Options API では props: { task: Object }、emits: ['toggle']、this.$emit('toggle', id) と書きます。親から見た使い方(:task と @toggle)はまったく同じなので、コンポーネント単位で少しずつ <script setup> に移せます。
まとめ
- propsは親→子、emitは子→親。データは親が持ち、一方向に流す
- Vue 3.5 は defineProps の分割代入がリアクティブで、既定値もそのまま書ける
- propsを直接書き換えると
target is readonly警告が出て値は変わらない。オブジェクトの中身は警告なしで変わるので emit で親に任せる - emit の実行時検証は警告だけで、イベント自体は届く
- vue-tsc で必須漏れ・型違い・未宣言イベントを検出。親側の未宣言イベントは strictTemplates で検出
- 兄弟は親に状態を持ち上げ、孫以下は provide / inject、画面をまたぐなら Pinia
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Vueのpropsとemitの使い方|親子のデータ受け渡しを実行結果で解説」
株式会社RJCでは、コンポーネントの設計やデータの流れを、コードレビューの中で仲間と一緒に考えています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。