diff --git a/src/web/app/desktop/-tags/home-widgets/activity.tag b/src/web/app/desktop/-tags/home-widgets/activity.tag
deleted file mode 100644
index 878de6d13..000000000
--- a/src/web/app/desktop/-tags/home-widgets/activity.tag
+++ /dev/null
@@ -1,32 +0,0 @@
-<mk-activity-home-widget>
-	<mk-activity-widget design={ data.design } view={ data.view } user={ I } ref="activity"/>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-	</style>
-	<script lang="typescript">
-		this.data = {
-			view: 0,
-			design: 0
-		};
-
-		this.mixin('widget');
-
-		this.initializing = true;
-
-		this.on('mount', () => {
-			this.$refs.activity.on('view-changed', view => {
-				this.data.view = view;
-				this.save();
-			});
-		});
-
-		this.func = () => {
-			if (++this.data.design == 3) this.data.design = 0;
-			this.$refs.activity.update({
-				design: this.data.design
-			});
-			this.save();
-		};
-	</script>
-</mk-activity-home-widget>
diff --git a/src/web/app/desktop/-tags/home-widgets/post-form.tag b/src/web/app/desktop/-tags/home-widgets/post-form.tag
deleted file mode 100644
index 8564cdf02..000000000
--- a/src/web/app/desktop/-tags/home-widgets/post-form.tag
+++ /dev/null
@@ -1,103 +0,0 @@
-<mk-post-form-home-widget>
-	<mk-post-form v-if="place == 'main'"/>
-	<template v-if="place != 'main'">
-		<template v-if="data.design == 0">
-			<p class="title">%fa:pencil-alt%%i18n:desktop.tags.mk-post-form-home-widget.title%</p>
-		</template>
-		<textarea disabled={ posting } ref="text" onkeydown={ onkeydown } placeholder="%i18n:desktop.tags.mk-post-form-home-widget.placeholder%"></textarea>
-		<button @click="post" disabled={ posting }>%i18n:desktop.tags.mk-post-form-home-widget.post%</button>
-	</template>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-			background #fff
-			overflow hidden
-			border solid 1px rgba(0, 0, 0, 0.075)
-			border-radius 6px
-
-			> .title
-				z-index 1
-				margin 0
-				padding 0 16px
-				line-height 42px
-				font-size 0.9em
-				font-weight bold
-				color #888
-				box-shadow 0 1px rgba(0, 0, 0, 0.07)
-
-				> [data-fa]
-					margin-right 4px
-
-			> textarea
-				display block
-				width 100%
-				max-width 100%
-				min-width 100%
-				padding 16px
-				margin-bottom 28px + 16px
-				border none
-				border-bottom solid 1px #eee
-
-			> button
-				display block
-				position absolute
-				bottom 8px
-				right 8px
-				margin 0
-				padding 0 10px
-				height 28px
-				color $theme-color-foreground
-				background $theme-color !important
-				outline none
-				border none
-				border-radius 4px
-				transition background 0.1s ease
-				cursor pointer
-
-				&:hover
-					background lighten($theme-color, 10%) !important
-
-				&:active
-					background darken($theme-color, 10%) !important
-					transition background 0s ease
-
-	</style>
-	<script lang="typescript">
-		this.data = {
-			design: 0
-		};
-
-		this.mixin('widget');
-
-		this.func = () => {
-			if (++this.data.design == 2) this.data.design = 0;
-			this.save();
-		};
-
-		this.onkeydown = e => {
-			if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post();
-		};
-
-		this.post = () => {
-			this.update({
-				posting: true
-			});
-
-			this.$root.$data.os.api('posts/create', {
-				text: this.$refs.text.value
-			}).then(data => {
-				this.clear();
-			}).catch(err => {
-				alert('失敗した');
-			}).then(() => {
-				this.update({
-					posting: false
-				});
-			});
-		};
-
-		this.clear = () => {
-			this.$refs.text.value = '';
-		};
-	</script>
-</mk-post-form-home-widget>
diff --git a/src/web/app/desktop/views/components/widgets/activity.vue b/src/web/app/desktop/views/components/widgets/activity.vue
new file mode 100644
index 000000000..8bf45a556
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/activity.vue
@@ -0,0 +1,31 @@
+<template>
+<mk-activity
+	:design="props.design"
+	:init-view="props.view"
+	:user="os.i"
+	@view-changed="viewChanged"/>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+	name: 'activity',
+	props: {
+		design: 0,
+		view: 0
+	}
+}).extend({
+	methods: {
+		func() {
+			if (this.props.design == 2) {
+				this.props.design = 0;
+			} else {
+				this.props.design++;
+			}
+		},
+		viewChanged(view) {
+			this.props.view = view;
+		}
+	}
+});
+</script>
diff --git a/src/web/app/desktop/views/components/widgets/post-form.vue b/src/web/app/desktop/views/components/widgets/post-form.vue
new file mode 100644
index 000000000..c32ad5761
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/post-form.vue
@@ -0,0 +1,109 @@
+<template>
+<div class="mkw-post-form">
+	<template v-if="data.design == 0">
+		<p class="title">%fa:pencil-alt%%i18n:desktop.tags.mk-post-form-home-widget.title%</p>
+	</template>
+	<textarea :disabled="posting" v-model="text" @keydown="onKeydown" placeholder="%i18n:desktop.tags.mk-post-form-home-widget.placeholder%"></textarea>
+	<button @click="post" :disabled="posting">%i18n:desktop.tags.mk-post-form-home-widget.post%</button>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+export default define({
+	name: 'post-form',
+	props: {
+		design: 0
+	}
+}).extend({
+	data() {
+		return {
+			posting: false,
+			text: ''
+		};
+	},
+	methods: {
+		func() {
+			if (this.props.design == 1) {
+				this.props.design = 0;
+			} else {
+				this.props.design++;
+			}
+		},
+		onKeydown(e) {
+			if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post();
+		},
+		post() {
+			this.posting = true;
+
+			(this as any).api('posts/create', {
+				text: this.text
+			}).then(data => {
+				this.clear();
+			}).catch(err => {
+				alert('失敗した');
+			}).then(() => {
+				this.posting = false;
+			});
+		},
+		clear() {
+			this.text = '';
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-post-form
+	background #fff
+	overflow hidden
+	border solid 1px rgba(0, 0, 0, 0.075)
+	border-radius 6px
+
+	> .title
+		z-index 1
+		margin 0
+		padding 0 16px
+		line-height 42px
+		font-size 0.9em
+		font-weight bold
+		color #888
+		box-shadow 0 1px rgba(0, 0, 0, 0.07)
+
+		> [data-fa]
+			margin-right 4px
+
+	> textarea
+		display block
+		width 100%
+		max-width 100%
+		min-width 100%
+		padding 16px
+		margin-bottom 28px + 16px
+		border none
+		border-bottom solid 1px #eee
+
+	> button
+		display block
+		position absolute
+		bottom 8px
+		right 8px
+		margin 0
+		padding 0 10px
+		height 28px
+		color $theme-color-foreground
+		background $theme-color !important
+		outline none
+		border none
+		border-radius 4px
+		transition background 0.1s ease
+		cursor pointer
+
+		&:hover
+			background lighten($theme-color, 10%) !important
+
+		&:active
+			background darken($theme-color, 10%) !important
+			transition background 0s ease
+
+</style>