WordPress 7.1 Icon API: Đăng Ký SVG Icon Tùy Chỉnh Cho Plugin Từ A Đến Z

Câu trả lời nhanh
WordPress 7.1 chính thức có Icon API công khai: dùng wp_register_icon_collection() tạo bộ icon riêng và wp_register_icon() thêm từng SVG icon, chạy trên hook init. Icon hiện ngay trong Icon Library của editor, render bằng wp_get_icon() và đọc được qua REST API. Lưu ý sanitizer chỉ cho thẻ svg, path, polygon và không hỗ trợ stroke, nên dùng icon kiểu fill-based.

Từ WordPress 7.1, việc thêm SVG icon tùy chỉnh vào site không còn là “nghệ thuật hack CSS” nữa. Core giờ có một API công khai: wp_register_icon_collection()wp_register_icon(). Bạn đăng ký icon một lần, dùng được ngay trong Icon block của editor, render bằng PHP qua wp_get_icon(), thậm chí đọc qua REST API. Mình đã thử dựng một plugin icon nhỏ theo hướng dẫn chính thức trên Developer Blog của WordPress, và trong bài này mình sẽ đi lại toàn bộ từng bước, kèm cả những giới hạn mà tài liệu ít ai nói rõ.

Icon API trong WordPress 7.1 là gì?

WordPress 7.1 Icon API đăng ký SVG icon tùy chỉnh cho plugin
WordPress 7.1 Icon API đăng ký SVG icon tùy chỉnh cho plugin

Đây là bộ API công khai mới trong WordPress 7.1 cho phép plugin và theme đăng ký bộ sưu tập SVG icon riêng. Trước đó, Icon block ra mắt ở bản 7.0 chỉ dùng được bộ icon có sẵn của core, danh sách khá hạn chế. Từ 7.1, bạn tạo collection của mình, thêm icon vào, và toàn bộ hiện ra ngay trong Icon Library của editor. Đây là thay đổi lớn với ai làm theme hay plugin thương mại, vì trước đây muốn đổi icon người ta phải nhúng icon font, CSS pseudo-elements, hoặc chặn SVG của core — vừa fragile vừa khó bảo trì.

Cách đăng ký icon đơn giản nhất như thế nào?

Bạn cần hai hàm: một hàm tạo collection, một hàm thêm icon vào collection. Tất cả chạy trên hook init. Đoạn code tối thiểu dưới đây mình đặt vào functions.php của theme hoặc file plugin, nó tạo collection “Restaurant” và một icon Cake:

add_action('init', 'tt_restaurant_icons_register');

function tt_restaurant_icons_register(): void
{
    wp_register_icon_collection('tt-restaurant', [
        'label'       => __('Restaurant', 'tt-icons'),
        'description' => __('Bo icon nha hang cua theme cua minh.', 'tt-icons')
    ]);

    wp_register_icon('tt-restaurant/cake', [
        'label'   => __('Cake', 'tt-icons'),
        'content' => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="#1f1f1f"><path d="M160-80q-17 0-28.5-11.5T120-120v-200q0-33 23.5-56.5T200-400v-160q0-33 23.5-56.5T280-640h160v-58q-18-12-29-29t-11-41q0-15 6-29.5t18-26.5l56-56 56 56q12 12 18 26.5t6 29.5q0 24-11 41t-29 29v58h160q33 0 56.5 23.5T760-560v160q33 0 56.5 23.5T840-320v200q0 17-11.5 28.5T800-80H160Z"/></svg>'
    ]);
}

Vài điểm mình rút ra khi làm thật. Thứ nhất, slug collection nên có prefix của plugin hoặc theme để tránh đụng nhau, ví dụ tt-restaurant. Thứ hai, tên icon bắt buộc theo định dạng collection-slug/icon-slug, thiếu dấu gạch chéo là WordPress im lặng không đăng ký. Thứ ba, bạn chọn một trong hai thuộc tính: content (SVG inline như trên) hoặc file_path (đường dẫn tuyệt đối tới file .svg). Với hơn 3-4 icon, mình khuyên dùng file_path cho gọn code.

Dựng plugin icon hoàn chỉnh gồm những bước nào?

Mình đi theo đúng pattern mà Justin Tadlock hướng dẫn trên Developer Blog, dùng plugin thật thay vì nhét code vào theme. Cấu trúc thư mục như sau:

tt-restaurant-icons/
├── plugin.php
├── public/
│   └── icon/
│       ├── bakery.svg
│       ├── bento.svg
│       ├── cake.svg
│       ├── dinner.svg
│       ├── ramen.svg
│       └── restaurant.svg
└── src/
    ├── Icon.php
    └── IconRegistrar.php

Các file SVG mình lấy từ kho Material Icons của Google, tải về đặt vào public/icon. Điểm thú vị của tutorial gốc là thay vì định nghĩa icon bằng mảng chuỗi, tác giả dùng string-backed enum của PHP 8.1. Mình làm theo và thấy đúng là đáng: IDE gợi ý đúng tên icon, gõ sai là báo lỗi ngay lúc code chứ không đợi tới runtime.

File src/Icon.php như sau:

<?php
declare(strict_types=1);

namespace TTRestaurantIcons;

enum Icon: string
{
    case Bakery     = 'bakery';
    case Cake       = 'cake';
    case Dinner     = 'dinner';
    case Ramen      = 'ramen';
    case Restaurant = 'restaurant';

    public const COLLECTION = 'tt-restaurant';

    private const ICONS_PATH = PLUGIN_DIR . '/public/icon';

    public function label(): string
    {
        return match ($this) {
            self::Bakery     => __('Bakery', 'tt-icons'),
            self::Cake       => __('Cake', 'tt-icons'),
            self::Dinner     => __('Dinner', 'tt-icons'),
            self::Ramen      => __('Ramen', 'tt-icons'),
            self::Restaurant => __('Restaurant', 'tt-icons')
        };
    }

    public function handle(): string
    {
        return self::COLLECTION . '/' . $this->value;
    }

    public function filePath(): string
    {
        return self::ICONS_PATH . '/' . $this->value . '.svg';
    }
}

Tiếp theo là lớp đăng ký, file src/IconRegistrar.php. Lớp này chỉ làm đúng một việc: loop qua tất cả case của enum và gọi wp_register_icon():

<?php
declare(strict_types=1);

namespace TTRestaurantIcons;

final class IconRegistrar
{
    public function boot(): void
    {
        add_action('init', $this->register(...));
    }

    private function register(): void
    {
        wp_register_icon_collection(Icon::COLLECTION, [
            'label'       => __('Restaurant', 'tt-icons'),
            'description' => __('Bo icon nha hang.', 'tt-icons')
        ]);

        foreach (Icon::cases() as $icon) {
            wp_register_icon($icon->handle(), [
                'label'     => $icon->label(),
                'file_path' => $icon->filePath()
            ]);
        }
    }
}

Cuối cùng, trong plugin.php bạn define hằng PLUGIN_DIR, require hai file src rồi bootstrap:

define('PLUGIN_DIR', __DIR__);

require_once PLUGIN_DIR . '/src/Icon.php';
require_once PLUGIN_DIR . '/src/IconRegistrar.php';

add_action('plugins_loaded', static function (): void {
    (new IconRegistrar())->boot();
});

Kích hoạt plugin, mở editor, chèn Icon block, bấm Replace — bạn sẽ thấy tab mới mang tên collection của mình xuất hiện trong Icon Library cùng toàn bộ icon vừa đăng ký. Nếu không thấy tab, 90% là do bạn quên chạy đăng ký trên hook init, hoặc gõ sai slug collection giữa hai lời gọi hàm.

Render icon ngoài editor bằng cách nào?

Trong nội dung block, icon tham chiếu qua block markup, ví dụ <!-- wp:icon {"icon":"tt-restaurant/cake"} /-->. Nhưng nếu viết template cho classic theme hoặc xuất HTML từ plugin, bạn dùng hàm wp_get_icon():

<?= wp_get_icon(Icon::Cake->handle(), ['size' => 32]) ?>

Kết hợp với phương thức handle() của enum, bạn không bao giờ phải gõ tay chuỗi tt-restaurant/cake ở hai nơi khác nhau. Ngoài ra core cũng expose sẵn endpoint REST API chỉ đọc cho icon, nên nếu bạn làm headless hoặc cần danh sách icon cho JS bên ngoài, cứ gọi endpoint thay vì tự viết route riêng.

Những giới hạn của Icon API cần biết trước?

Ba điểm này mình muốn ai định dùng API trong production phải nắm rõ, vì tài liệu chính chỉ nhắc nhẹ. Một, sanitizer hiện chỉ cho phép ba thẻ là svg, pathpolygon — mọi thẻ khác như circle hay rect đều bị strip sạch, nên icon vẽ bằng nhiều loại thẻ sẽ bị biến dạng. Hai, thuộc tính stroke bị loại bỏ, bạn phải dùng icon phong cách fill-based, còn fill ở thẻ svg ngoài cùng cũng bị strip và phải style màu qua CSS. Ba, chưa có React component chính thức để nhúng icon picker vào block khác của bạn, phần này đang trong kế hoạch cho 7.2 trở đi.

Góc nhìn của mình: dù còn giới hạn sanitizer, đây là thay đổi đáng để đầu tư ngay. So với thời phải tải Dashicons hay nhúng icon font, một API chuẩn của core nghĩa là icon dùng chung được giữa editor, template PHP và REST API, không phụ thuộc thư viện bên thứ ba. Ai đang duy trì plugin có custom post type hay block riêng, mình khuyên dựng một plugin icon nhỏ theo pattern trên từ bây giờ, tới khi 7.2 mở rộng allowlist thì chỉ cần thay file SVG.

Nếu bạn chưa nâng cấp lên WordPress 7.1, xem lại quy trình smoke test 5 bước sau khi nhấn Update mà mình chia sẻ trước đây, và đừng bỏ qua tổng hợp Field Guide WordPress 7.1 để nắm toàn bộ thay đổi hướng developer trong bản phát hành này.

Thanh Tùng

Mình là Thanh Tùng. Bạn bè gọi mình là "bác sĩ máy tính" vì hễ máy nào có vấn đề là mình muốn mò vào xem sao. Mình viết hướng dẫn theo cách mà mình mong người khác đã viết cho mình ngày xưa — từng bước rõ ràng, không bỏ sót, và nói luôn cái gì hay bị lỗi. Ngoài giờ làm mình chơi guitar, nuôi mèo, và có một con VPS riêng dành riêng cho việc cài thử đủ thứ linh tinh.

Xem tất cả bài viết →

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *