diff --git a/packages/backend/src/models/repositories/channel.ts b/packages/backend/src/models/repositories/channel.ts
index 857470f4ec..809129db6c 100644
--- a/packages/backend/src/models/repositories/channel.ts
+++ b/packages/backend/src/models/repositories/channel.ts
@@ -40,6 +40,7 @@ export const ChannelRepository = db.getRepository(Channel).extend({
 			name: channel.name,
 			description: channel.description,
 			userId: channel.userId,
+			bannerId: channel.bannerId,
 			bannerUrl: banner ? DriveFiles.getPublicUrl(banner, false) : null,
 			usersCount: channel.usersCount,
 			notesCount: channel.notesCount,
diff --git a/packages/backend/src/models/schema/channel.ts b/packages/backend/src/models/schema/channel.ts
index 67833cb0dd..d3ec222c8d 100644
--- a/packages/backend/src/models/schema/channel.ts
+++ b/packages/backend/src/models/schema/channel.ts
@@ -36,6 +36,13 @@ export const packedChannelSchema = {
 			nullable: true,
 			optional: false,
 		},
+		bannerId: {
+			type: "string",
+			optional: false,
+			nullable: true,
+			format: "id",
+			example: "xxxxxxxxxx",
+		},
 		notesCount: {
 			type: "number",
 			nullable: false,
@@ -57,5 +64,10 @@ export const packedChannelSchema = {
 			optional: false,
 			format: "id",
 		},
+		hasUnreadNote: {
+			type: "boolean",
+			optional: true,
+			nullable: false,
+		},
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts
index 0de7a837a1..fdd21da65f 100644
--- a/packages/backend/src/server/api/endpoints/channels/update.ts
+++ b/packages/backend/src/server/api/endpoints/channels/update.ts
@@ -83,7 +83,7 @@ export default define(meta, paramDef, async (ps, me) => {
 	await Channels.update(channel.id, {
 		...(ps.name !== undefined ? { name: ps.name } : {}),
 		...(ps.description !== undefined ? { description: ps.description } : {}),
-		...(banner ? { bannerId: banner.id } : {}),
+		...(banner ? { bannerId: banner.id } : { bannerId: null }),
 	});
 
 	return await Channels.pack(channel.id, me);
diff --git a/packages/client/src/pages/channel-editor.vue b/packages/client/src/pages/channel-editor.vue
index 60bcfe990e..3791a98d05 100644
--- a/packages/client/src/pages/channel-editor.vue
+++ b/packages/client/src/pages/channel-editor.vue
@@ -50,6 +50,7 @@ import { useRouter } from "@/router";
 import { definePageMetadata } from "@/scripts/page-metadata";
 import { i18n } from "@/i18n";
 import icon from "@/scripts/icon";
+import type { entities } from "firefish-js";
 
 const router = useRouter();
 
@@ -57,26 +58,24 @@ const props = defineProps<{
 	channelId?: string;
 }>();
 
-const channel = ref(null);
-const name = ref(null);
-const description = ref(null);
+const channel = ref<entities.Channel | null>(null);
+const name = ref<string>("");
+const description = ref<string>("");
 const bannerUrl = ref<string | null>(null);
 const bannerId = ref<string | null>(null);
 
-watch(
-	() => bannerId.value,
-	async () => {
-		if (bannerId.value == null) {
-			bannerUrl.value = null;
-		} else {
-			bannerUrl.value = (
-				await os.api("drive/files/show", {
-					fileId: bannerId.value,
-				})
-			).url;
-		}
-	},
-);
+let bannerUrlUpdated = false;
+
+/**
+ * Set banner url and id when we already know the url
+ * Prevent redundant network requests from being sent
+ */
+function setBanner(opt: { bannerId: string | null; bannerUrl: string | null }) {
+	bannerUrlUpdated = true;
+	bannerUrl.value = opt.bannerUrl;
+	bannerId.value = opt.bannerId;
+	bannerUrlUpdated = false;
+}
 
 async function fetchChannel() {
 	if (props.channelId == null) return;
@@ -86,23 +85,44 @@ async function fetchChannel() {
 	});
 
 	name.value = channel.value.name;
-	description.value = channel.value.description;
-	bannerId.value = channel.value.bannerId;
-	bannerUrl.value = channel.value.bannerUrl;
+	description.value = channel.value.description ?? "";
+	setBanner(channel.value);
 }
 
-fetchChannel();
+await fetchChannel();
+
+watch(bannerId, async () => {
+	if (bannerUrlUpdated) {
+		bannerUrlUpdated = false;
+		return;
+	}
+	if (bannerId.value == null) {
+		bannerUrl.value = null;
+	} else {
+		bannerUrl.value = (
+			await os.api("drive/files/show", {
+				fileId: bannerId.value,
+			})
+		).url;
+	}
+});
 
 function save() {
-	const params = {
+	const params: {
+		name: string;
+		description: string;
+		bannerId: string | null;
+	} = {
 		name: name.value,
 		description: description.value,
 		bannerId: bannerId.value,
 	};
 
 	if (props.channelId) {
-		params.channelId = props.channelId;
-		os.api("channels/update", params).then(() => {
+		os.api("channels/update", {
+			...params,
+			channelId: props.channelId,
+		}).then(() => {
 			os.success();
 		});
 	} else {
@@ -113,14 +133,20 @@ function save() {
 	}
 }
 
-function setBannerImage(evt) {
+function setBannerImage(evt: MouseEvent) {
 	selectFile(evt.currentTarget ?? evt.target, null).then((file) => {
-		bannerId.value = file.id;
+		setBanner({
+			bannerId: file.id,
+			bannerUrl: file.url,
+		});
 	});
 }
 
 function removeBannerImage() {
-	bannerId.value = null;
+	setBanner({
+		bannerId: null,
+		bannerUrl: null,
+	});
 }
 
 const headerActions = computed(() => []);